carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "bytes"
5 "context"
6 "errors"
7 "os"
8 "testing"
9
10 "github.com/carlosframework/platform/internal/provision"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14func TestPlan9B8MigrationCannotReplaySequencedRecord(t *testing.T) {
15 t.Setenv("CARLOS_HOST_CLAIM", "1")
16 ctx := context.Background()
17 for _, family := range []string{"instances", "sidecars"} {
18 t.Run(family, func(t *testing.T) {
19 store := release.NewDirStore(t.TempDir())
20 key := "apps/bab/app/" + family + "/record.json"
21 legacy := []byte("{\"schema\": 1}\n")
22 if _, err := putMigrationObject(ctx, store, key, legacy); err != nil {
23 t.Fatal(err)
24 }
25 raw, _, err := store.Get(ctx, key)
26 if err != nil || !bytes.Equal(raw, legacy) {
27 t.Fatalf("legacy bytes: %s %v", raw, err)
28 }
29 key = "apps/bab/app/" + family + "/new.json"
30 if _, err := putMigrationObject(ctx, store, key, []byte(`{"record_seq":9}`)); !errors.Is(err, provision.ErrRecordSequence) {
31 t.Fatalf("replayed sequence: %v", err)
32 }
33 if _, _, err := store.Get(ctx, key); !errors.Is(err, os.ErrNotExist) {
34 t.Fatalf("replay wrote: %v", err)
35 }
36 })
37 }
38}
39
40func TestPlan9B8MigrationPreflightRefusesBeforeAnyCopy(t *testing.T) {
41 t.Setenv("CARLOS_HOST_CLAIM", "1")
42 ctx := context.Background()
43 store := release.NewDirStore(t.TempDir())
44 source := "apps/bab/app/instances/source.json"
45 dest := "apps/cac/app/instances/source.json"
46 for _, body := range []string{`{"record_seq":0}`, `{"record_seq":9}`} {
47 if err := store.Delete(ctx, source); err != nil {
48 t.Fatal(err)
49 }
50 if _, err := store.Put(ctx, source, []byte(body), ""); err != nil {
51 t.Fatal(err)
52 }
53 plan := []copyOp{{from: "apps/bab/app/channel.json", to: "apps/cac/app/channel.json"}, {from: source, to: dest}}
54 if err := validateMigrationRecords(ctx, store, plan); !errors.Is(err, provision.ErrRecordSequence) {
55 t.Fatalf("preflight: %v", err)
56 }
57 keys, err := store.List(ctx, "apps/cac/")
58 if err != nil || len(keys) != 0 {
59 t.Fatalf("preflight wrote: %v %v", keys, err)
60 }
61 }
62}
63
64// Claim mode refuses a present record_seq; legacy bodies without one
65// still pass preflight and are copied byte for byte.
66func TestPlan9M1MigrationToleratesBodiesMainCopied(t *testing.T) {
67 t.Setenv("CARLOS_HOST_CLAIM", "1")
68 ctx := context.Background()
69 for name, body := range map[string]string{
70 "malformed": `{"schema": 1`,
71 "array": `[1,2]`,
72 "empty": ``,
73 "duplicate": `{"a":1,"a":2}`,
74 "text": "not json\n",
75 "open-seq": `{"record_seq":1`,
76 } {
77 t.Run(name, func(t *testing.T) {
78 store := release.NewDirStore(t.TempDir())
79 source := "apps/bab/app/instances/source.json"
80 dest := "apps/cac/app/instances/source.json"
81 if _, err := store.Put(ctx, source, []byte(body), ""); err != nil {
82 t.Fatal(err)
83 }
84 if err := validateMigrationRecords(ctx, store, []copyOp{{from: source, to: dest}}); err != nil {
85 t.Fatalf("preflight refused a body main copied: %v", err)
86 }
87 if _, err := putMigrationObject(ctx, store, dest, []byte(body)); err != nil {
88 t.Fatalf("copy: %v", err)
89 }
90 raw, _, err := store.Get(ctx, dest)
91 if err != nil || !bytes.Equal(raw, []byte(body)) {
92 t.Fatalf("bytes changed: %q %v", raw, err)
93 }
94 })
95 }
96 for name, body := range map[string]string{
97 "duplicate-seq": `{"record_seq":1,"record_seq":2}`,
98 "case-seq": `{"Record_Seq":1}`,
99 } {
100 t.Run(name, func(t *testing.T) {
101 store := release.NewDirStore(t.TempDir())
102 dest := "apps/cac/app/instances/source.json"
103 if _, err := putMigrationObject(ctx, store, dest, []byte(body)); !errors.Is(err, provision.ErrRecordSequence) {
104 t.Fatalf("sequence-bearing body: %v", err)
105 }
106 })
107 }
108}
109