package delivery import ( "strings" "testing" ) func artifactOf(name, kind, digest, reference string) BuiltArtifact { return BuiltArtifact{Name: name, Kind: kind, Digest: digest, Reference: reference} } // A done key names where each artifact is (novox/hq ADR 0498 clauses 1, 5 and 6; the lead's decision L1 of R4e): a // bundle or an archive by its artifact-store blob, an image or an upstream copy by the registry's reference to its // digest (or the kept artifact-store form), a package by name and version. A reference of the wrong form for its kind, // or naming another digest or artifact, is refused, naming the artifact. func TestADoneKeyNamesEachArtifactsReferenceInTheFormOfItsKind(t *testing.T) { d := "sha256:" + digestOf('6') good := []BuiltArtifact{ artifactOf("code", "bundle", d, "artifact-store://mail/code/blobs/"+d), artifactOf("files", "archive", d, "artifact-store://mail/files/blobs/"+d), artifactOf("server", "image", d, "10.0.0.1:5000/mail/server@"+d), artifactOf("server", "image", d, "artifact-store://mail/server@"+d), artifactOf("nats", "upstream", d, "registry.example/nats/nats@"+d), artifactOf("lib", "package", d, "@novox/mesh-sdk@0.1.15"), artifactOf("lib", "package", d, "left-pad@1.3.0"), artifactOf("old", "", d, "anything at all"), } for _, a := range good { b := doneBuild() b.Artifacts = []BuiltArtifact{a} if _, err := EncodeBuilderBuild(b); err != nil { t.Errorf("%+v was refused: %v", a, err) } } other := "sha256:" + digestOf('7') bad := []BuiltArtifact{ artifactOf("code", "bundle", d, "artifact-store://mail/code/blobs/"+other), artifactOf("code", "bundle", d, "artifact-store://mail/other/blobs/"+d), artifactOf("code", "bundle", d, "10.0.0.1:5000/mail/code@"+d), artifactOf("server", "image", d, "10.0.0.1:5000/mail@"+d), artifactOf("server", "image", d, "10.0.0.1:5000/mail/server@"+other), artifactOf("server", "image", d, "mail/server@"+d), artifactOf("server", "image", d, "artifact-store://mail/server/blobs/"+d), artifactOf("lib", "package", d, "@novox/mesh-sdk"), artifactOf("lib", "package", d, "0.1.15"), } for _, a := range bad { b := doneBuild() b.Artifacts = []BuiltArtifact{a} if _, err := EncodeBuilderBuild(b); err == nil || !strings.Contains(err.Error(), `"`+a.Name+`"`) { t.Errorf("%+v: %v, wanted a refusal naming the artifact", a, err) } } } // The controller delivers only from a done key whose every artifact names a reference (ADR 0498 clause 8): Referenced // says so and names the artifacts without one. func TestReferencedNamesTheArtifactsWithoutAReference(t *testing.T) { d := "sha256:" + digestOf('6') b := doneBuild() b.Artifacts = []BuiltArtifact{ artifactOf("code", "bundle", d, "artifact-store://mail/code/blobs/"+d), artifactOf("server", "image", d, ""), } if ok, missing := b.Referenced(); ok || len(missing) != 1 || missing[0] != "server" { t.Fatalf("Referenced = %v, %v; want false, [server]", ok, missing) } b.Artifacts[1].Reference = "10.0.0.1:5000/mail/server@" + d if ok, missing := b.Referenced(); !ok || len(missing) != 0 { t.Fatalf("Referenced = %v, %v; want true", ok, missing) } if ok, _ := (BuilderBuild{State: BuildQueued}).Referenced(); ok { t.Fatal("a queued key is referenced") } } // The errors a build module answers open with a fixed prefix (ADR 0498 clauses 2, 4 and 5; ADR 0478 clause 2), and three // of them are not built again until the module's fingerprint changes; a failed push is. func TestOnlyAPushFailureIsBuiltAgainBeforeTheFingerprintChanges(t *testing.T) { for _, c := range []struct { err string again bool }{ {NotStored + "code: the store said 500", true}, {NotPinned + "nats: upstream names no digest", false}, {VersionTaken + "@novox/mesh-sdk@0.1.15 holds another file", false}, {RefusedKind + "a package in go", false}, {"go test: 2 failed", true}, } { if got := FailedUntilChanged(c.err); got == c.again { t.Errorf("FailedUntilChanged(%q) = %v", c.err, got) } } if NotStored != "not stored: " || NotPinned != "not pinned: " || VersionTaken != "version taken: " || RefusedKind != "refused kind: " { t.Fatal("a prefix changed its words") } } // A key whose module's lock names a version of the group's package not yet published waits, queued, saying which // (ADR 0500 clause 6): an older reader reads it as queued, and only a queued key waits. func TestAKeyWaitsQueuedForAPackageVersion(t *testing.T) { b := BuilderBuild{State: BuildQueued, Manifest: digestOf('4'), Waits: "@novox/mesh-sdk@0.1.15"} data, err := EncodeBuilderBuild(b) if err != nil { t.Fatal(err) } back, err := DecodeBuilderBuild(data) if err != nil || back.Waits != b.Waits || back.State != BuildQueued { t.Fatalf("decoded %+v, %v", back, err) } building := BuilderBuild{State: BuildBuilding, By: "x", Waits: "@novox/mesh-sdk@0.1.15"} if _, err := EncodeBuilderBuild(building); err == nil { t.Fatal("a building key that waits was accepted") } if _, err := EncodeBuilderBuild(BuilderBuild{State: BuildQueued, Waits: "mesh-sdk"}); err == nil { t.Fatal("a wait naming no version was accepted") } } // A module that builds no artifact gets a done key too (issue 1613), and it is delivered: Referenced is ok for a done // key with no artifact, since no artifact lacks a reference; a queued one with none still is not. func TestADoneKeyWithNoArtifactIsReferenced(t *testing.T) { b := BuilderBuild{State: BuildDone, Manifest: digestOf('4')} data, err := EncodeBuilderBuild(b) if err != nil { t.Fatalf("a done key with no artifact was refused: %v", err) } back, err := DecodeBuilderBuild(data) if err != nil { t.Fatal(err) } if ok, missing := back.Referenced(); !ok || len(missing) != 0 { t.Fatalf("Referenced = %v, %v; want true", ok, missing) } } // A Node fetch that finds the lock naming a package version the registry does not hold fails with WaitsFor; the // builder then writes the key queued with Waits, and the error is not one that waits for the fingerprint to change. func TestWaitsForIsAPrefixThatIsBuiltAgain(t *testing.T) { if WaitsFor != "waits for: " || FailedUntilChanged(WaitsFor+"@novox/mesh-sdk@0.1.15") { t.Fatalf("WaitsFor = %q", WaitsFor) } }