1package apps_test
2
3import (
4 "context"
5 "errors"
6 "testing"
7
8 "github.com/stretchr/testify/assert"
9 "github.com/stretchr/testify/require"
10
11 "github.com/bnema/gordon/internal/domain"
12 "github.com/bnema/gordon/internal/usecase/apps"
13)
14
15// fakeAppStateReader is an in-memory AppStateReader for lifecycle scenarios.
16type fakeAppStateReader struct {
17 active map[string]domain.AppActive
18 stopped map[string]bool
19 listErr error
20}
21
22func newFakeAppStateReader() *fakeAppStateReader {
23 return &fakeAppStateReader{active: map[string]domain.AppActive{}, stopped: map[string]bool{}}
24}
25
26func (f *fakeAppStateReader) ListApps(context.Context) ([]string, error) {
27 if f.listErr != nil {
28 return nil, f.listErr
29 }
30 names := make([]string, 0, len(f.active))
31 for name := range f.active {
32 names = append(names, name)
33 }
34 return names, nil
35}
36
37func (f *fakeAppStateReader) LoadActive(_ context.Context, app string) (domain.AppActive, bool, error) {
38 a, ok := f.active[app]
39 return a, ok, nil
40}
41
42func (f *fakeAppStateReader) LoadIntent(_ context.Context, app string) (domain.AppStopIntent, error) {
43 return domain.AppStopIntent{App: app, Stopped: f.stopped[app]}, nil
44}
45
46func activeWith(app string, containers ...string) domain.AppActive {
47 services := map[string]domain.AppEffectiveService{}
48 for i, c := range containers {
49 services[string(rune('a'+i))] = domain.AppEffectiveService{Container: c}
50 }
51 return domain.AppActive{App: app, Services: services}
52}
53
54func TestCountManagedContainers_Lifecycle(t *testing.T) {
55 ctx := context.Background()
56 state := newFakeAppStateReader()
57 count := func() int64 {
58 t.Helper()
59 n, err := apps.CountManagedContainers(ctx, state)
60 require.NoError(t, err)
61 return n
62 }
63
64 // Boot with no apps.
65 assert.Equal(t, int64(0), count())
66
67 // Boot hydration: persisted ACTIVE state is counted immediately.
68 state.active["web"] = activeWith("web", "c1", "c2")
69 state.active["db"] = activeWith("db", "c3")
70 assert.Equal(t, int64(3), count())
71
72 // Failed deploy of a new service leaves it without a container.
73 state.active["web"] = activeWith("web", "c1", "c2", "")
74 assert.Equal(t, int64(3), count())
75
76 // Stop excludes the app; start restores it.
77 state.stopped["web"] = true
78 assert.Equal(t, int64(1), count())
79 state.stopped["web"] = false
80 assert.Equal(t, int64(3), count())
81
82 // Remove retires the app state.
83 delete(state.active, "db")
84 assert.Equal(t, int64(2), count())
85}
86
87func TestCountManagedContainers_Error(t *testing.T) {
88 state := newFakeAppStateReader()
89 state.listErr = errors.New("boom")
90 _, err := apps.CountManagedContainers(context.Background(), state)
91 require.ErrorContains(t, err, "boom")
92}