Provisions were named after roles: provides "database", requires "database". Nothing distinguished engines, so a module written against PostgreSQL could be matched to a provider of SQL Server, resolve as satisfied, deploy, and fail on its first query — with nothing connecting that error back to a match made elsewhere by something that believed it had done its job. The failure is in the direction that hides. Refusing on ambiguity exists precisely so this does not happen, and the generic name walked around it: with one provider of each name nothing is ambiguous, so nothing is asked. How it got in: every resolver test had exactly one provider per name, so no mismatch was expressible and none was caught. The fixtures agreed with the design — the same fault as the imagined test output in 04-ISSUES/005, at the level of a name. Refused rather than documented, because the old naming *was* the documented convention. Providing database/db/sql/sql-database is now a parse error naming what to write instead. The rule is about coupling, not specificity everywhere: route and resolver stay role-named, because a consumer genuinely cannot tell which proxy answered. novox/hq ADR 0027.
40 lines
1.6 KiB
Go
40 lines
1.6 KiB
Go
package catalogue
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// A provision names what the consumer is coupled to (novox/hq ADR 0027).
|
|
//
|
|
// The fault this defends against does not look like a fault: resolution succeeds, the module
|
|
// deploys, and the first query fails somewhere else entirely. Every earlier test had exactly one
|
|
// provider of each name, so no mismatch was expressible and none was caught.
|
|
func TestAModuleMayNotProvideAGenericDatabase(t *testing.T) {
|
|
for _, hidden := range []string{"database", "db", "sql", "sql-database"} {
|
|
_, err := ParseManifest([]byte(`{"module":"postgres","version":"1",
|
|
"provides":[{"name":"` + hidden + `","scope":"mesh"}]}`))
|
|
if err == nil {
|
|
t.Fatalf("providing %q was accepted; a requirement for it matches any engine", hidden)
|
|
}
|
|
for _, want := range []string{hidden, "postgres-database", "first query"} {
|
|
if !strings.Contains(err.Error(), want) {
|
|
t.Errorf("refusing %q did not mention %q, so nobody learns what to write:\n%v",
|
|
hidden, want, err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// The rule is about coupling, not about specificity everywhere. Naming `route` after a particular
|
|
// proxy would be the same error in the other direction.
|
|
func TestARoleNameIsFineWhereTheConsumerCannotTellTheDifference(t *testing.T) {
|
|
for _, role := range []string{"route", "resolver", "artifact-store", "postgres-database"} {
|
|
if _, err := ParseManifest([]byte(`{"module":"m","version":"1",
|
|
"provides":[{"name":"` + role + `","scope":"mesh"}]}`)); err != nil {
|
|
t.Errorf("providing %q was refused, and it names what a consumer actually gets: %v",
|
|
role, err)
|
|
}
|
|
}
|
|
}
|