Test placement read only from the placement, and raise a store before a migration (issue 651)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,7 @@ func placeable() Manifest {
|
||||
}
|
||||
|
||||
func placedBy(values map[string]any) Rendering {
|
||||
return Rendering{Settings: SettingsBy{"arr": {{From: "node anchor", Values: values}}}}
|
||||
return Rendering{Settings: SettingsBy{"arr": {LayerOf("node anchor", values)}}}
|
||||
}
|
||||
|
||||
func TestAnAssignmentPlacesDirectoriesAndAccesses(t *testing.T) {
|
||||
@@ -179,3 +179,43 @@ func TestAnUnknownAccessReferenceIsRefusedAtParse(t *testing.T) {
|
||||
t.Fatalf("an access with no id and no path was accepted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// **Placement is read from the placement, never from a layer's values** (novox/hq ADR 0348 clause 11, issue
|
||||
// 651). A `places` or `accesses` among the config values places nothing; the same in Placement does. A
|
||||
// mergeable file of today's form still takes every key of the layer as it is said, placement too, so it
|
||||
// composes as it did while placement was kept in the layer.
|
||||
func TestPlacementIsReadFromThePlacementOnly(t *testing.T) {
|
||||
m := placeable()
|
||||
said := map[string]any{
|
||||
PlacesSetting: map[string]any{"config": "/services/arr/config"},
|
||||
AccessesSetting: map[string]any{"series": "/storage/media/series", "spool": "/storage/downloads"},
|
||||
"log": "warn",
|
||||
}
|
||||
among := []Layer{{From: "anchor", Values: said}}
|
||||
if placed, err := Places(m, among); err != nil || placed != nil {
|
||||
t.Fatalf("a place among the config values placed %v (%v)", placed, err)
|
||||
}
|
||||
if _, _, err := accessesFor(m, among); err == nil {
|
||||
t.Fatal("an access among the config values placed it")
|
||||
}
|
||||
beside := []Layer{LayerOf("anchor", said)}
|
||||
if beside[0].Values["log"] != "warn" || beside[0].Values[PlacesSetting] != nil || beside[0].Placement[PlacesSetting] == nil {
|
||||
t.Fatalf("LayerOf split %+v", beside[0])
|
||||
}
|
||||
if placed, err := Places(m, beside); err != nil || placed["config"].Path != "/services/arr/config" {
|
||||
t.Fatalf("the placement did not place: %v (%v)", placed, err)
|
||||
}
|
||||
|
||||
file := map[string]any{"id": "runtime", "type": "file", "path": "/x", "content": `{"a":1}`, "merge": "json"}
|
||||
whole, err := ApplySettings(file, among)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
split, err := ApplySettings(file, beside)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if whole["content"] != split["content"] || !strings.Contains(split["content"].(string), `"places"`) {
|
||||
t.Fatalf("today's mergeable file composes %q, was %q", split["content"], whole["content"])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package inventory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
@@ -75,3 +76,94 @@ func (i *Inventory) RemoveNodeForTest(ctx context.Context, name string) (bool, e
|
||||
}
|
||||
return tag.RowsAffected() == 1, nil
|
||||
}
|
||||
|
||||
// ForTestBefore is ForTest with the store migrated only below migration `number`, and a function that runs the
|
||||
// rest: for a test of a migration that moves data, which seeds the store as the mesh holds it before the
|
||||
// migration, migrates, and reads what the migration made of it (novox/hq issue 651).
|
||||
func ForTestBefore(t *testing.T, number int) (*Inventory, func()) {
|
||||
t.Helper()
|
||||
admin := os.Getenv("MESH_TEST_POSTGRES")
|
||||
if admin == "" {
|
||||
t.Skip("no MESH_TEST_POSTGRES; run `make check` to raise one")
|
||||
}
|
||||
name := fmt.Sprintf("invb_%d_%s", time.Now().UnixNano()%1_000_000,
|
||||
strings.ToLower(strings.NewReplacer("/", "", "-", "").Replace(t.Name())))
|
||||
if len(name) > 60 {
|
||||
name = name[:60]
|
||||
}
|
||||
all, err := Migrations()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var before []store.Migration
|
||||
for _, m := range all {
|
||||
if m.Number < number {
|
||||
before = append(before, m)
|
||||
}
|
||||
}
|
||||
store.CreateMigratedForTest(t, admin, name, before)
|
||||
busPasswordCostForTest.Store(int32(bcrypt.MinCost))
|
||||
cut := strings.LastIndex(admin, "/")
|
||||
t.Setenv(store.Variable(Name), admin[:cut]+"/"+name+"?sslmode=disable")
|
||||
inv, err := Open(t.Context())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
inv.Close()
|
||||
c, err := pgx.Connect(context.Background(), admin)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer c.Close(context.Background())
|
||||
_, _ = c.Exec(context.Background(), "drop database if exists "+name+" with (force)")
|
||||
})
|
||||
if err := inv.Ready(t.Context(), 20*time.Second); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return inv, func() {
|
||||
t.Helper()
|
||||
if _, err := inv.store.Migrate(t.Context(), all); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ExecForTest runs one statement against a test's store, for a fixture no verb of the mesh writes: a row as an
|
||||
// older migration left it.
|
||||
func (i *Inventory) ExecForTest(t *testing.T, sql string, args ...any) {
|
||||
t.Helper()
|
||||
if _, err := i.store.Pool().Exec(t.Context(), sql, args...); err != nil {
|
||||
t.Fatalf("%s: %v", sql, err)
|
||||
}
|
||||
}
|
||||
|
||||
// QueryForTest reads rows of a test's store, each row's columns as JSON text, for a test that checks what a
|
||||
// migration left.
|
||||
func (i *Inventory) QueryForTest(t *testing.T, sql string, args ...any) [][]string {
|
||||
t.Helper()
|
||||
rows, err := i.store.Pool().Query(t.Context(), sql, args...)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: %v", sql, err)
|
||||
}
|
||||
defer rows.Close()
|
||||
var out [][]string
|
||||
for rows.Next() {
|
||||
values, err := rows.Values()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
row := make([]string, len(values))
|
||||
for k, v := range values {
|
||||
row[k] = fmt.Sprint(v)
|
||||
if b, err := json.Marshal(v); err == nil {
|
||||
row[k] = string(b)
|
||||
}
|
||||
}
|
||||
out = append(out, row)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user