Skip to content

Commit e5b8200

Browse files
authored
Merge pull request #285 from shelltime/claude/quirky-bohr-b0cue8
feat(model): report storage engine in tracking metadata
2 parents 07544ff + 50465bd commit e5b8200

7 files changed

Lines changed: 52 additions & 1 deletion

File tree

daemon/handlers.track_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package daemon
22

33
import (
44
"context"
5+
"encoding/json"
56
"net/http"
67
"net/http/httptest"
78
"testing"
@@ -23,6 +24,8 @@ type fakeCommandStore struct {
2324

2425
cursorSetCalls int
2526
pruneCalls int
27+
28+
engine string
2629
}
2730

2831
func (f *fakeCommandStore) SavePre(ctx context.Context, cmd model.Command, rt time.Time) error {
@@ -72,6 +75,13 @@ func (f *fakeCommandStore) Prune(ctx context.Context, cursor time.Time) error {
7275
return nil
7376
}
7477

78+
func (f *fakeCommandStore) Engine() string {
79+
if f.engine == "" {
80+
return model.StorageEngineFile
81+
}
82+
return f.engine
83+
}
84+
7585
func (f *fakeCommandStore) Close() error { return nil }
7686

7787
// fakeConfigService implements model.ConfigService without mockery.
@@ -184,12 +194,14 @@ func (s *TrackHandlerTestSuite) TestTrackPostNotEnoughToFlush() {
184194
}
185195

186196
func (s *TrackHandlerTestSuite) TestTrackPostFlushSyncsAndPrunes() {
197+
var sentPayload model.PostTrackArgs
187198
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
199+
_ = json.NewDecoder(r.Body).Decode(&sentPayload)
188200
w.WriteHeader(http.StatusNoContent)
189201
}))
190202
defer server.Close()
191203

192-
store := &fakeCommandStore{noCursorExist: true}
204+
store := &fakeCommandStore{noCursorExist: true, engine: model.StorageEngineBolt}
193205
commandStore = store
194206
stConfig = fakeConfigService{cfg: model.ShellTimeConfig{Token: "t", APIEndpoint: server.URL, FlushCount: 1}}
195207

@@ -204,6 +216,9 @@ func (s *TrackHandlerTestSuite) TestTrackPostFlushSyncsAndPrunes() {
204216
assert.Len(s.T(), store.post, 1)
205217
assert.Equal(s.T(), 1, store.cursorSetCalls)
206218
assert.Equal(s.T(), 1, store.pruneCalls)
219+
// the payload must report the engine that buffered the commands
220+
assert.Equal(s.T(), model.StorageEngineBolt, sentPayload.Meta.CliEngine)
221+
assert.Equal(s.T(), 1, sentPayload.Meta.Source, "daemon path must mark source as daemon")
207222
}
208223

209224
func (s *TrackHandlerTestSuite) TestTrackPostFallsBackToFileStore() {

model/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ type TrackingMetaData struct {
3232
Terminal string `json:"terminal,omitempty"`
3333
Multiplexer string `json:"multiplexer,omitempty"`
3434

35+
// CliEngine is the storage engine that buffered these commands:
36+
// StorageEngineFile or StorageEngineBolt.
37+
CliEngine string `json:"cliEngine,omitempty"`
38+
3539
// 0: cli, 1: daemon
3640
Source int `json:"source"`
3741
}

model/store.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ type CommandStore interface {
4242
// before the cursor), keeping unfinished pre commands.
4343
Prune(ctx context.Context, cursor time.Time) error
4444

45+
// Engine reports which storage engine backs this store (StorageEngineFile
46+
// or StorageEngineBolt). It is attached to sync metadata so the server
47+
// knows how the commands were buffered.
48+
Engine() string
49+
4550
// Close releases any resources held by the store (no-op for fileStore).
4651
Close() error
4752
}

model/store_bolt.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,8 @@ func (s *boltStore) Prune(ctx context.Context, cursor time.Time) error {
241241
})
242242
}
243243

244+
func (s *boltStore) Engine() string { return StorageEngineBolt }
245+
244246
func (s *boltStore) Close() error {
245247
if s.db == nil {
246248
return nil

model/store_file.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,6 @@ func (s *fileStore) Prune(ctx context.Context, cursor time.Time) error {
155155
return os.WriteFile(GetCursorFilePath(), []byte(fmt.Sprintf("%d", cursor.UnixNano())), 0644)
156156
}
157157

158+
func (s *fileStore) Engine() string { return StorageEngineFile }
159+
158160
func (s *fileStore) Close() error { return nil }

model/tracking_build.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func BuildTrackingData(ctx context.Context, store CommandStore, config ShellTime
5656
meta := TrackingMetaData{
5757
OS: sysInfo.Os,
5858
OSVersion: sysInfo.Version,
59+
CliEngine: store.Engine(),
5960
}
6061

6162
trackingData := make([]TrackingData, 0)

model/tracking_build_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ func TestBuildTrackingData(t *testing.T) {
3232
require.Equal(t, 42, res.Data[0].PPID)
3333
require.Equal(t, "h", res.Meta.Hostname)
3434
require.Equal(t, "bash", res.Meta.Shell)
35+
require.Equal(t, StorageEngineBolt, res.Meta.CliEngine)
36+
}
37+
38+
func TestBuildTrackingDataFileEngine(t *testing.T) {
39+
t.Setenv("HOME", t.TempDir())
40+
InitFolder("") // reset globals to the default .shelltime under the temp HOME
41+
42+
store := NewFileStore()
43+
defer store.Close()
44+
45+
ctx := context.Background()
46+
start := time.Now()
47+
cmd := Command{Shell: "zsh", SessionID: 3, Command: "ls -la", Username: "u", Hostname: "h", Time: start}
48+
require.NoError(t, store.SavePre(ctx, cmd, start))
49+
post := cmd
50+
post.Time = start.Add(time.Second)
51+
require.NoError(t, store.SavePost(ctx, post, 0, post.Time))
52+
53+
res, err := BuildTrackingData(ctx, store, ShellTimeConfig{})
54+
require.NoError(t, err)
55+
require.Len(t, res.Data, 1)
56+
require.Equal(t, StorageEngineFile, res.Meta.CliEngine)
3557
}
3658

3759
func TestBuildTrackingDataExcludes(t *testing.T) {

0 commit comments

Comments
 (0)