package signedjobs import ( "context" "errors" "io" "log/slog" "os" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-agent/internal/localapi" "gitea.dooplex.hu/admin/felhom-agent/internal/reconcile" ) // add_internal_place (felhom.eu architecture/16 ยง9, agent v0.158.0): the operator's job for an OLD one-disk box. type ipRunner struct { out, stderr string err error calls [][]string } func (r *ipRunner) Run(_ context.Context, name string, args ...string) ([]byte, []byte, error) { r.calls = append(r.calls, append([]string{name}, args...)) return []byte(r.out), []byte(r.stderr), r.err } func (r *ipRunner) RunStdin(ctx context.Context, _ io.Reader, name string, args ...string) ([]byte, []byte, error) { return r.Run(ctx, name, args...) } func ipExec(t *testing.T, r *ipRunner) (*InternalPlaceExecutor, string, *int) { t.Helper() file := filepath.Join(t.TempDir(), localapi.InternalPlaceFile) e := NewInternalPlaceExecutor(r, file, slog.New(slog.NewTextHandler(io.Discard, nil))) n := 0 e.SetEnsure(func(context.Context) { n++ }) return e, file, &n } func TestAddInternalPlace_RunsTheHelperWritesTheRecordAndMounts(t *testing.T) { r := &ipRunner{out: "created uuid=0b6e4c5a-1d2e-4f3a-9b8c-7d6e5f4a3b2c gib=33\n"} e, file, ensured := ipExec(t, r) if err := e.Execute(context.Background(), "add_internal_place", []byte(`{}`)); err != nil { t.Fatal(err) } if len(r.calls) != 1 || strings.Join(r.calls[0], " ") != InternalPlaceHelper+" create 0" { t.Fatalf("the helper must be called exactly as `create 0`, got %v", r.calls) } rec, ok, err := localapi.ReadInternalPlace(file) if err != nil || !ok || rec.FSUUID != "0b6e4c5a-1d2e-4f3a-9b8c-7d6e5f4a3b2c" || rec.GiB != 33 || rec.MadeBy != "signed-job" { t.Fatalf("record: %+v ok=%v err=%v", rec, ok, err) } if *ensured != 1 { t.Fatalf("the mount must be asked for once, got %d", *ensured) } } func TestAddInternalPlace_NoRoomOrRefusalOrGarbage_WritesNothing(t *testing.T) { for name, r := range map[string]*ipRunner{ "skipped": {out: "skipped only 7 GiB free for it (pool 53, reserve 16, promised 102, planned 0; needs 20)"}, "refused": {err: errors.New("exit status 1"), stderr: "felhom-internal-place: REFUSED: holds something"}, "garbage": {out: "created uuid=../../etc gib=1"}, } { e, file, ensured := ipExec(t, r) if err := e.Execute(context.Background(), "add_internal_place", nil); err == nil { t.Fatalf("%s: want an error", name) } if _, statErr := os.Stat(file); !os.IsNotExist(statErr) || *ensured != 0 { t.Fatalf("%s: no record and no mount may follow, stat=%v ensured=%d", name, statErr, *ensured) } } } func TestAddInternalPlace_TakesNoParametersAndIgnoresOtherOps(t *testing.T) { r := &ipRunner{out: "created uuid=0b6e4c5a-1d2e-4f3a-9b8c-7d6e5f4a3b2c gib=33"} e, _, _ := ipExec(t, r) if err := e.Execute(context.Background(), "add_internal_place", []byte(`{"gib":900}`)); err == nil || len(r.calls) != 0 { t.Fatalf("a size from the signer must be refused before the helper runs, err=%v calls=%v", err, r.calls) } if err := e.Execute(context.Background(), "decommission", []byte(`{}`)); !errors.Is(err, ErrNoExecutor) { t.Fatalf("another op: want ErrNoExecutor, got %v", err) } } func TestAddInternalPlace_IsSignatureOnly(t *testing.T) { if got := reconcile.Classify(reconcile.ClassAddInternalPlace, reconcile.Provenance{}); got != reconcile.Destructive { t.Fatalf("add_internal_place must need an operator signature, classified %v", got) } if string(reconcile.ClassAddInternalPlace) != opAddInternalPlace { t.Fatalf("op string drift: %q vs %q", reconcile.ClassAddInternalPlace, opAddInternalPlace) } }