| new file mode 100644 |
| index 000000000..2d013ef9b |
| --- /dev/null |
| +++ b/internal/hostbox/work_test.go |
| @@ -0,0 +1,116 @@ |
| +//go:build unix |
| + |
| +package hostbox |
| + |
| +import ( |
| + "context" |
| + "errors" |
| + "os" |
| + "path/filepath" |
| + "testing" |
| + "time" |
| + |
| + "github.com/carlosframework/platform/internal/hostclaim" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| + "github.com/carlosframework/platform/internal/hostwrite" |
| + "github.com/carlosframework/platform/internal/release" |
| +) |
| + |
| +func workFixture(t *testing.T) (WorkSource, release.Store, hostclaim.Observed) { |
| + t.Helper() |
| + ctx := context.Background() |
| + dir := t.TempDir() |
| + locks := filepath.Join(dir, "locks") |
| + for _, p := range []string{locks, filepath.Join(dir, "hostguard")} { |
| + if err := os.Mkdir(p, 0770); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + store := release.NewDirStore(t.TempDir()) |
| + obs, err := hostclaim.Read(ctx, store, "shop.example") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + obs, err = hostclaim.Update(ctx, store, obs, hostclaim.Adopt(hostclaim.NewHolder{Family: "instance", Key: "apps/acct/app/instances/shop.example.json", Account: "acct", App: "app", Host: "shop.example", Region: "ie"}, "box", nil, nil, "adopt", func() string { return "id" })) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + source := WorkSource{Guard: hostguard.Guard{DataDir: dir, LockDir: locks, Claims: hostguard.StoreReader{Store: store}}, Box: "box", Region: "ie", Now: func() time.Time { return time.Unix(100, 0) }} |
| + source.Policy = func(_ context.Context, c *hostclaim.Claim, s Selection, _ hostwrite.Identity) error { |
| + return Serving(c, s) |
| + } |
| + source.Local = func(context.Context, *hostclaim.Claim, Selection) error { return nil } |
| + if err := source.Guard.Activate(ctx, "shop.example", "box", nil); err != nil { |
| + t.Fatal(err) |
| + } |
| + return source, store, obs |
| +} |
| + |
| +func TestSelectedWorkRejectsChangesAndNeverRecaptures(t *testing.T) { |
| + for _, mode := range []string{"phase", "executor", "round-trip", "lease", "held"} { |
| + t.Run(mode, func(t *testing.T) { |
| + source, store, obs := workFixture(t) |
| + localLost, heldChanged := false, false |
| + source.Local = func(context.Context, *hostclaim.Claim, Selection) error { |
| + if localLost { |
| + return ErrUnsupported |
| + } |
| + return nil |
| + } |
| + source.Policy = func(_ context.Context, c *hostclaim.Claim, s Selection, i hostwrite.Identity) error { |
| + if heldChanged || i.Config != "selected" { |
| + return ErrChanged |
| + } |
| + return Serving(c, s) |
| + } |
| + ctx := context.Background() |
| + work, err := source.Select(ctx, "shop.example", "config") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + writes := 0 |
| + if err := work.With(ctx, hostwrite.Identity{Config: "selected"}, func() error { writes++; return nil }); err != nil { |
| + t.Fatal(err) |
| + } |
| + switch mode { |
| + case "phase": |
| + _, err = hostclaim.Update(ctx, store, obs, hostclaim.BeginDelete("delete", "test", "etag", time.Unix(1, 0), []string{"ie"}, []string{"box"})) |
| + case "executor": |
| + _, err = hostclaim.Update(ctx, store, obs, func(c *hostclaim.Claim) error { c.Op.Exec = "other"; c.Op.Participants = []string{"other"}; return nil }) |
| + case "round-trip": |
| + _, err = hostclaim.Update(ctx, store, obs, hostclaim.AdmitWriter(hostclaim.Admission{Box: "box", Mode: "cold", NewGeneration: "next", Region: "ie", PredecessorRegion: "ie"})) |
| + if err == nil { |
| + err = source.Guard.Activate(ctx, "shop.example", "box", nil) |
| + } |
| + case "lease": |
| + localLost = true |
| + case "held": |
| + heldChanged = true |
| + } |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := work.With(ctx, hostwrite.Identity{Config: "selected"}, func() error { writes++; return nil }); err == nil { |
| + t.Fatal("stale write admitted") |
| + } |
| + if writes != 1 { |
| + t.Fatal(writes) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestSelectedWorkNeedsLocalRecordAndProviders(t *testing.T) { |
| + source, _, _ := workFixture(t) |
| + source.Local = nil |
| + if _, err := source.Select(context.Background(), "shop.example", "env"); !errors.Is(err, ErrUnsupported) { |
| + t.Fatal(err) |
| + } |
| + source.Local = func(context.Context, *hostclaim.Claim, Selection) error { return nil } |
| + if err := os.Remove(filepath.Join(source.Guard.DataDir, "hostguard", "shop.example.json")); err != nil { |
| + t.Fatal(err) |
| + } |
| + if _, err := source.Select(context.Background(), "shop.example", "env"); !errors.Is(err, hostguard.ErrNoRecord) { |
| + t.Fatal(err) |
| + } |
| +} |