diff --git a/.gitignore b/.gitignore index df65af5..58099d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .claude/ CLAUDE.md +.pi/ # Test and coverage coverage.out diff --git a/exprenv_test.go b/exprenv_test.go index 2e3cb29..e10366e 100644 --- a/exprenv_test.go +++ b/exprenv_test.go @@ -30,9 +30,15 @@ func TestArgExprEnv_JSONSerialization(t *testing.T) { t.Parallel() env := zapscript.ArgExprEnv{ - Platform: "mister", - Version: "2.0.0", - ScanMode: "hold", + Platform: "mister", + Version: "2.0.0", + ScanMode: "hold", + MediaPlaying: true, + MediaReady: true, + Hook: zapscript.ExprEnvHook{ + Name: "startup", + FirstBootStart: true, + }, Device: zapscript.ExprEnvDevice{ Hostname: "mister", OS: "linux", @@ -43,7 +49,6 @@ func TestArgExprEnv_JSONSerialization(t *testing.T) { Value: "**launch:snes/mario", Data: "extra-data", }, - MediaPlaying: true, ActiveMedia: zapscript.ExprEnvActiveMedia{ LauncherID: "retroarch", SystemID: "snes", @@ -63,6 +68,9 @@ func TestArgExprEnv_JSONSerialization(t *testing.T) { assert.Contains(t, jsonStr, `"version"`, "should contain version field") assert.Contains(t, jsonStr, `"scan_mode"`, "should contain scan_mode field") assert.Contains(t, jsonStr, `"media_playing"`, "should contain media_playing field") + assert.Contains(t, jsonStr, `"media_ready"`, "should contain media_ready field") + assert.Contains(t, jsonStr, `"hook"`, "should contain hook field") + assert.Contains(t, jsonStr, `"first_boot_start"`, "should contain first_boot_start field") assert.Contains(t, jsonStr, `"active_media"`, "should contain active_media field") assert.Contains(t, jsonStr, `"last_scanned"`, "should contain last_scanned field") assert.Contains(t, jsonStr, `"launcher_id"`, "should contain launcher_id field") @@ -85,6 +93,11 @@ func TestArgExprEnv_JSONRoundTrip(t *testing.T) { Version: "1.0.0", ScanMode: "tap", MediaPlaying: true, + MediaReady: true, + Hook: zapscript.ExprEnvHook{ + Name: "startup", + FirstBootStart: true, + }, Device: zapscript.ExprEnvDevice{ Hostname: "testhost", OS: "linux", @@ -115,6 +128,9 @@ func TestArgExprEnv_JSONRoundTrip(t *testing.T) { assert.Equal(t, original.Version, decoded.Version) assert.Equal(t, original.ScanMode, decoded.ScanMode) assert.Equal(t, original.MediaPlaying, decoded.MediaPlaying) + assert.Equal(t, original.MediaReady, decoded.MediaReady) + assert.Equal(t, original.Hook.Name, decoded.Hook.Name) + assert.Equal(t, original.Hook.FirstBootStart, decoded.Hook.FirstBootStart) assert.Equal(t, original.Device.Hostname, decoded.Device.Hostname) assert.Equal(t, original.LastScanned.ID, decoded.LastScanned.ID) assert.Equal(t, original.ActiveMedia.Path, decoded.ActiveMedia.Path) @@ -158,6 +174,22 @@ func TestExprEnvLaunching_JSONSerialization(t *testing.T) { assert.Contains(t, jsonStr, `"launcher_id"`) } +func TestExprEnvHook_JSONSerialization(t *testing.T) { + t.Parallel() + + hook := zapscript.ExprEnvHook{ + Name: "startup", + FirstBootStart: true, + } + + jsonBytes, err := json.Marshal(hook) + require.NoError(t, err) + + jsonStr := string(jsonBytes) + assert.Contains(t, jsonStr, `"name"`) + assert.Contains(t, jsonStr, `"first_boot_start"`) +} + // TestArgExprEnv_EmptyFieldsSerialization verifies that empty struct fields are serialized // (no omitempty behavior) for consistent JSON structure in external scripts. func TestArgExprEnv_EmptyFieldsSerialization(t *testing.T) { diff --git a/expressions.go b/expressions.go index 483938a..5aabcac 100644 --- a/expressions.go +++ b/expressions.go @@ -61,6 +61,14 @@ type ExprEnvLaunching struct { LauncherID string `expr:"launcher_id" json:"launcher_id"` } +// ExprEnvHook represents hook context for expression evaluation. +// +//nolint:tagliatelle // JSON uses snake_case to match expression env naming +type ExprEnvHook struct { + Name string `expr:"name" json:"name"` + FirstBootStart bool `expr:"first_boot_start" json:"first_boot_start"` +} + //nolint:tagliatelle // JSON uses snake_case to match expression env naming type ArgExprEnv struct { ActiveMedia ExprEnvActiveMedia `expr:"active_media" json:"active_media"` @@ -71,7 +79,9 @@ type ArgExprEnv struct { Platform string `expr:"platform" json:"platform"` Version string `expr:"version" json:"version"` ScanMode string `expr:"scan_mode" json:"scan_mode"` + Hook ExprEnvHook `expr:"hook" json:"hook,omitempty"` MediaPlaying bool `expr:"media_playing" json:"media_playing"` + MediaReady bool `expr:"media_ready" json:"media_ready"` } //nolint:tagliatelle // JSON uses snake_case to match expression env naming diff --git a/parser_test.go b/parser_test.go index f7c6d2f..352b109 100644 --- a/parser_test.go +++ b/parser_test.go @@ -398,6 +398,21 @@ func TestPostProcess(t *testing.T) { input: "something " + zapscript.TokExpStart + "true" + zapscript.TokExprEnd, want: `something true`, }, + { + name: "test expression hook name", + input: zapscript.TokExpStart + "hook.name" + zapscript.TokExprEnd, + want: `startup`, + }, + { + name: "test expression hook first boot start", + input: zapscript.TokExpStart + "hook.first_boot_start" + zapscript.TokExprEnd, + want: `true`, + }, + { + name: "test expression media ready", + input: zapscript.TokExpStart + "media_ready" + zapscript.TokExprEnd, + want: `true`, + }, { name: "bad return type", input: zapscript.TokExpStart + "device" + zapscript.TokExprEnd, @@ -422,7 +437,12 @@ func TestPostProcess(t *testing.T) { Platform: "mister", Version: "1.2.3", MediaPlaying: true, + MediaReady: true, ScanMode: "tap", + Hook: zapscript.ExprEnvHook{ + Name: "startup", + FirstBootStart: true, + }, Device: zapscript.ExprEnvDevice{ Hostname: "test-device", OS: "linux",