package seeds import ( "context" "errors" "sync" "testing" "github.com/carlosframework/platform/internal/release" ) func TestReadFloorAbsentIsNil(t *testing.T) { s := release.NewDirStore(t.TempDir()) f, err := ReadFloor(context.Background(), s) if f != nil || err != nil { t.Fatalf("absent floor = %+v, %v; want nil, nil", f, err) } if _, err := s.Put(context.Background(), FloorKey, []byte("{not json"), ""); err != nil { t.Fatal(err) } if _, err := ReadFloor(context.Background(), s); err == nil { t.Fatal("garbage at the floor key read as no floor") } } func TestPublishFloorOnlyRises(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: "v0.27.0"}); err != nil { t.Fatal(err) } if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: "v0.27.0"}); err != nil { t.Fatalf("the same floor again: %v", err) } var low *FloorLowerError if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: "v0.26.9"}); !errors.As(err, &low) || low.Current != "v0.27.0" { t.Fatalf("a lower floor: err = %v, want FloorLowerError naming v0.27.0", err) } if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: "v0.28.0"}); err != nil { t.Fatalf("a higher floor: %v", err) } f, err := ReadFloor(ctx, s) if err != nil || f == nil || f.MinRelease != "v0.28.0" || f.Min != RequiredProtocol { t.Fatalf("floor = %+v, %v", f, err) } if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: "v0.29.0-rc.1"}); err == nil { t.Fatal("a prerelease floor published") } } func TestPublishFloorLowerProtocolRefused(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) if err := PublishFloor(ctx, s, Floor{Min: 2, MinRelease: "v0.27.0"}); err != nil { t.Fatal(err) } var low *FloorLowerError if err := PublishFloor(ctx, s, Floor{Min: 1, MinRelease: "v0.28.0"}); !errors.As(err, &low) { t.Fatalf("a lower protocol on a higher release: err = %v, want FloorLowerError", err) } if f, err := ReadFloor(ctx, s); err != nil || *f != (Floor{Min: 2, MinRelease: "v0.27.0"}) { t.Fatalf("floor = %+v, %v; want it unchanged", f, err) } } // Concurrent publishes of different floors must leave the highest one, never // a lower one that read the object before a higher one landed. func TestPublishFloorConcurrentLowerNeverWins(t *testing.T) { ctx := context.Background() for round := 0; round < 20; round++ { s := release.NewDirStore(t.TempDir()) versions := []string{"v0.27.0", "v0.28.0", "v0.29.0", "v0.30.0", "v0.31.0"} var wg sync.WaitGroup for _, v := range versions { wg.Add(1) go func() { defer wg.Done() var low *FloorLowerError if err := PublishFloor(ctx, s, Floor{Min: RequiredProtocol, MinRelease: v}); err != nil && !errors.As(err, &low) { t.Errorf("publish %s: %v", v, err) } }() } wg.Wait() f, err := ReadFloor(ctx, s) if err != nil || f == nil || f.MinRelease != "v0.31.0" { t.Fatalf("round %d: floor = %+v, %v; want v0.31.0", round, f, err) } } } func TestReleaseAtOrAbove(t *testing.T) { for _, c := range []struct { v, min string want bool err bool }{ {"v0.27.0", "v0.27.0", true, false}, {"v0.27.1", "v0.27.0", true, false}, {"v0.26.9", "v0.27.0", false, false}, {"v0.28.0-rc.1", "v0.27.0", false, true}, {"v0.27.0", "garbage", false, true}, } { got, err := ReleaseAtOrAbove(c.v, c.min) if got != c.want || (err != nil) != c.err { t.Errorf("ReleaseAtOrAbove(%s, %s) = %v, %v", c.v, c.min, got, err) } } } func TestProtocolMeetsWhatTheConsoleRequires(t *testing.T) { // The floor shipped dark through Plan 7 (preflight M9): spec ยง5.8's // move refusals, the seeds sweep and the legacy-routes assign door had // to exist before any door opens. Plan 8 built them, and its Task 6 // raised ProtocolVersion, so a box built from here can satisfy the // console's check once an operator publishes the seeds floor. if ProtocolVersion < RequiredProtocol { t.Fatalf("ProtocolVersion = %d, RequiredProtocol = %d", ProtocolVersion, RequiredProtocol) } }