1package app
2
3import (
4 "errors"
5 "fmt"
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/bnema/zerowrap"
11 "github.com/stretchr/testify/assert"
12 "github.com/stretchr/testify/require"
13)
14
15func TestNewKernel(t *testing.T) {
16 t.Parallel()
17
18 tmpDir := t.TempDir()
19 cfgPath := filepath.Join(tmpDir, "gordon.toml")
20 dataDir := filepath.Join(tmpDir, "data")
21
22 cfg := fmt.Sprintf(`[server]
23gordon_domain = "gordon.local"
24data_dir = %q
25
26[auth]
27enabled = false
28secrets_backend = "unsafe"
29`, dataDir)
30
31 err := os.WriteFile(cfgPath, []byte(cfg), 0o600)
32 require.NoError(t, err)
33
34 kernel, err := NewKernel(cfgPath)
35 require.NoError(t, err)
36 require.NotNil(t, kernel)
37 t.Cleanup(func() { require.NoError(t, kernel.Close()) })
38
39 require.NotNil(t, kernel.Config())
40}
41
42// TestKernelClose_QuiescenceTimeoutSkipsCleanup proves the fail-closed kernel
43// Close ordering: daemon-owned app administration is cancelled and joined on
44// a bounded context before any remaining kernel resource is torn down, and
45// when that quiescence times out the dependency teardown is skipped and the
46// error is returned, so an unfinished deploy is never dropped onto a closed
47// state or runtime.
48func TestKernelClose_QuiescenceTimeoutSkipsCleanup(t *testing.T) {
49 t.Parallel()
50
51 order := []string{}
52 admin := &orderRecordingAdmin{order: &order, err: errors.New("in-flight deploy did not unwind")}
53 cleanedUp := false
54 kernel := &Kernel{
55 appAdmin: admin,
56 log: zerowrap.Default(),
57 cleanup: func() { cleanedUp = true; order = append(order, "cleanup") },
58 }
59
60 require.Error(t, kernel.Close())
61 require.Equal(t, []string{"app-shutdown"}, order, "teardown must stop when quiescence times out")
62 assert.False(t, cleanedUp)
63 assert.True(t, admin.deadline, "app administration shutdown must run on a bounded context")
64}
65
66// TestKernelClose_CleansUpAfterQuiescence keeps the normal path: once app
67// administration joined cleanly, the kernel still tears its resources down.
68func TestKernelClose_CleansUpAfterQuiescence(t *testing.T) {
69 t.Parallel()
70
71 order := []string{}
72 admin := &orderRecordingAdmin{order: &order}
73 kernel := &Kernel{
74 appAdmin: admin,
75 log: zerowrap.Default(),
76 cleanup: func() { order = append(order, "cleanup") },
77 }
78
79 require.NoError(t, kernel.Close())
80 require.Equal(t, []string{"app-shutdown", "cleanup"}, order)
81}
82
83// TestKernelClose_WithoutAppAdministrationStillCleansUp keeps the minimal
84// kernel path (no full service wiring) closing without a nil-interface trap.
85func TestKernelClose_WithoutAppAdministrationStillCleansUp(t *testing.T) {
86 t.Parallel()
87
88 cleaned := false
89 kernel := &Kernel{log: zerowrap.Default(), cleanup: func() { cleaned = true }}
90
91 require.NoError(t, kernel.Close())
92 assert.True(t, cleaned)
93}