feat: Phase 7 — SeedService, SecretsService, ProvisioningService + ssh Provisioner

Seed ingest/export moves behind ports.SeedRepository (SeedRepo in the
postgres adapter; knowledge ingest absorbed from internal/knowledge,
package deleted). pct_create flow (defaults, template/VMID/gateway
pre-flights, pct create, graph registration) moves from httpapi's
approved-execution path into app.ProvisioningService + the ssh
provisioner adapter; CLI seed/export/secret become adapters over the
services. EntityCreateInput gains EnrolledAt. Plan status corrected:
phases 0-7 shipped, 8 + 9 gates open. VERSION 0.34.1.
This commit is contained in:
2026-08-16 09:11:26 +02:00
parent cd44501fa9
commit 60c0432d8b
26 changed files with 1565 additions and 689 deletions

View File

@@ -0,0 +1,65 @@
package db
import (
"reflect"
"testing"
)
// Tests for the seed-helper functions absorbed from internal/knowledge
// (Phase 7).
func TestSeedStr(t *testing.T) {
cases := []struct {
name string
m map[string]any
key string
want string
}{
{"missing key", map[string]any{}, "nope", ""},
{"string value", map[string]any{"k": "v"}, "k", "v"},
{"int value", map[string]any{"k": 42}, "k", ""},
{"nil value", map[string]any{"k": nil}, "k", ""},
{"empty string", map[string]any{"k": ""}, "k", ""},
}
for _, c := range cases {
if got := seedStr(c.m, c.key); got != c.want {
t.Errorf("%s: seedStr(%v, %q) = %q, want %q", c.name, c.m, c.key, got, c.want)
}
}
}
func TestSeedStrSlice(t *testing.T) {
cases := []struct {
name string
m map[string]any
key string
want []string
}{
{"missing key", map[string]any{}, "tags", nil},
{"string list", map[string]any{"tags": []any{"a", "b"}}, "tags", []string{"a", "b"}},
{"mixed list drops non-strings", map[string]any{"tags": []any{"a", 1, "b"}}, "tags", []string{"a", "b"}},
{"empty list", map[string]any{"tags": []any{}}, "tags", nil},
}
for _, c := range cases {
if got := seedStrSlice(c.m, c.key); !reflect.DeepEqual(got, c.want) {
t.Errorf("%s: seedStrSlice(%v, %q) = %v, want %v", c.name, c.m, c.key, got, c.want)
}
}
}
func TestToPGArray(t *testing.T) {
cases := []struct {
in []string
want string
}{
{nil, "{}"},
{[]string{}, "{}"},
{[]string{"ops"}, `{"ops"}`},
{[]string{"ops", "network"}, `{"ops","network"}`},
}
for _, c := range cases {
if got := toPGArray(c.in); got != c.want {
t.Errorf("toPGArray(%v) = %q, want %q", c.in, got, c.want)
}
}
}