-
Notifications
You must be signed in to change notification settings - Fork 10
fix: respect OS safe mode in plugin loader #1775
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- also adds util for reading ini configs synchronously
WalkthroughChanges introduce a safe-mode detection system with Changes
Sequence Diagram(s)sequenceDiagram
participant Consumer as Client Code
participant ISM as isSafeModeEnabled()
participant Store as Redux Store
participant FSL as loadStateFileSync()
participant FS as File System
Consumer->>ISM: Check safe mode
ISM->>Store: Get state.emhttp.var.safeMode
alt safeMode is boolean
Store-->>ISM: Return value
ISM-->>Consumer: Boolean result
else safeMode undefined/missing
ISM->>FSL: Load var state from file
FSL->>FS: Read var.ini from states dir
alt File found
FS-->>FSL: Content loaded
FSL->>FSL: Parse ini content
FSL->>Store: Dispatch updateEmhttpState
FSL-->>ISM: Return parsed state
ISM-->>Consumer: Boolean(varState.safeMode)
else File missing or error
FS-->>FSL: Error/null
FSL-->>ISM: null
ISM-->>Consumer: false (default)
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1775 +/- ##
==========================================
+ Coverage 52.69% 52.80% +0.10%
==========================================
Files 865 867 +2
Lines 49346 49434 +88
Branches 4952 4974 +22
==========================================
+ Hits 26003 26103 +100
+ Misses 23270 23258 -12
Partials 73 73 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
This plugin has been deployed to Cloudflare R2 and is available for testing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
api/src/unraid-api/plugin/__test__/plugin.service.test.ts (1)
6-11: Consider exposing test hooks instead of casting to private API.While the type casting approach works, consider adding a reset method or test-only hooks to
PluginServiceto avoid coupling tests to internal implementation details.Apply this pattern in the service:
// In plugin.service.ts static resetForTesting() { if (process.env.NODE_ENV === 'test') { PluginService.plugins = undefined; } }api/src/store/services/state-file-loader.ts (1)
78-80: Consider logging errors before returning null.The catch block silently returns
nullwithout logging the error. This might make debugging difficult if state file loading fails during bootstrap.Consider adding logging:
} catch (error) { + // Could add logging here if needed for debugging + // stateFileLogger.debug('Failed to load state file %s: %s', stateFileKey, error); return null; }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
api/src/core/utils/__test__/safe-mode.test.ts(1 hunks)api/src/core/utils/safe-mode.ts(1 hunks)api/src/store/modules/emhttp.ts(2 hunks)api/src/store/services/__test__/state-file-loader.test.ts(1 hunks)api/src/store/services/state-file-loader.ts(1 hunks)api/src/unraid-api/plugin/__test__/plugin.service.test.ts(1 hunks)api/src/unraid-api/plugin/plugin.service.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/{store,stores}/**/*.ts
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
In store files, explicitly import Vue reactivity utilities (e.g., computed, ref, watchEffect); do not rely on Nuxt auto-imports
Files:
api/src/store/modules/emhttp.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/store/services/state-file-loader.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use TypeScript import specifiers with .js extensions for ESM compatibility
Never use the any type; prefer precise typing
Avoid type casting; model proper types from the start
Files:
api/src/store/modules/emhttp.tsapi/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/safe-mode.tsapi/src/store/services/state-file-loader.tsapi/src/unraid-api/plugin/plugin.service.tsapi/src/core/utils/__test__/safe-mode.test.ts
api/**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
cache-manager v7 TTL values must be in milliseconds (e.g., 600000 for 10 minutes)
Files:
api/src/store/modules/emhttp.tsapi/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/safe-mode.tsapi/src/store/services/state-file-loader.tsapi/src/unraid-api/plugin/plugin.service.tsapi/src/core/utils/__test__/safe-mode.test.ts
api/src/unraid-api/**
📄 CodeRabbit inference engine (.cursor/rules/api-rules.mdc)
Prefer adding new files to the Nest repo at api/src/unraid-api/ instead of legacy code
Files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/unraid-api/plugin/plugin.service.ts
api/**/*.{test,spec}.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/api-rules.mdc)
api/**/*.{test,spec}.{js,jsx,ts,tsx}: Use Vitest for tests in the api; do not use Jest
Prefer not to mock simple dependencies in tests
For error testing, use.rejects.toThrow()without arguments; avoid asserting exact error messages unless the message format is the subject under test
Files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
{**/*.test.ts,**/__test__/{components,store}/**/*.ts}
📄 CodeRabbit inference engine (.cursor/rules/web-testing-rules.mdc)
{**/*.test.ts,**/__test__/{components,store}/**/*.ts}: Use .rejects.toThrow() without arguments when asserting that async functions throw; avoid checking exact error message strings unless the message format is explicitly under test
Focus tests on observable behavior and outcomes, not implementation details such as exact error messages
Use await nextTick() for DOM update assertions and flushPromises() for complex async chains; always await async operations before asserting
Place module mock declarations (vi.mock) at the top level of the test file to avoid hoisting issues
Use factory functions in vi.mock calls to define mocks and avoid hoisting pitfalls
Use vi.spyOn() to specify return values or behavior of methods under test
Reset/clear mocks between tests using vi.clearAllMocks() (and vi.resetAllMocks() when appropriate) to ensure isolation
Do not rely on Nuxt auto-imports in tests; import required Vue utilities explicitly in test files
Remember that vi.mock calls are hoisted; avoid mixing mock declarations and module mocks incorrectly
Files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
api/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
api/**/*.{test,spec}.{ts,tsx}: API test suite is Vitest; do not use Jest
Prefer not to mock simple dependencies in API tests
Files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
{api,web}/**/*.{test,spec}.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
{api,web}/**/*.{test,spec}.{ts,tsx}: For error tests, use .rejects.toThrow() without arguments; avoid asserting exact error messages unless that format is the subject
Focus tests on behavior, not implementation details
Avoid brittle tests tied to exact error or log wording
Use mocks as nouns, not verbs
Always await async operations before making assertions
Place all mock declarations at the top level; use factory functions for module mocks; clear mocks between tests
Files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
🧠 Learnings (24)
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/components/**/*.ts : Mock external dependencies and services in component tests (e.g., vi.mock for helper modules)
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,tsx} : Mock external dependencies and services in component tests
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : In store tests, mock external dependencies used by the store and verify interactions with those mocks
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use factory functions in vi.mock calls to define mocks and avoid hoisting pitfalls
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/mocks/**/*.ts : Frequently used mocks can also be placed under __test__/mocks
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to {api,web}/**/*.{test,spec}.{ts,tsx} : Place all mock declarations at the top level; use factory functions for module mocks; clear mocks between tests
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to api/**/*.{test,spec}.{ts,tsx} : Prefer not to mock simple dependencies in API tests
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to {api,web}/**/*.{test,spec}.{ts,tsx} : Focus tests on behavior, not implementation details
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : In store tests, verify action side effects and state changes, and assert that actions are called with the correct parameters
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Remember that vi.mock calls are hoisted; avoid mixing mock declarations and module mocks incorrectly
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to api/**/*.{test,spec}.{ts,tsx} : API test suite is Vitest; do not use Jest
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Reset/clear mocks between tests using vi.clearAllMocks() (and vi.resetAllMocks() when appropriate) to ensure isolation
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Use vi.spyOn() to specify return values or behavior of methods under test
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: This Nuxt app is tested with vitest outside the Nuxt runtime; structure tests accordingly
Applied to files:
api/src/unraid-api/plugin/__test__/plugin.service.test.tsapi/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : Thoroughly test async store actions: assert intermediate loading state, await completion, and check final state
Applied to files:
api/src/store/services/__test__/state-file-loader.test.tsapi/src/store/services/state-file-loader.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,tsx} : Let stores initialize with natural default state; don’t mock the store under test
Applied to files:
api/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : Do not mock the store under test; allow the store to initialize with its natural default state
Applied to files:
api/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to **/__test__/store/**/*.ts : Test computed properties/getters by accessing them directly and verifying derived state
Applied to files:
api/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Place module mock declarations (vi.mock) at the top level of the test file to avoid hoisting issues
Applied to files:
api/src/store/services/__test__/state-file-loader.test.tsapi/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,tsx} : Use createTestingPinia() for mocking stores in components
Applied to files:
api/src/store/services/__test__/state-file-loader.test.ts
📚 Learning: 2025-02-24T14:51:21.328Z
Learnt from: elibosley
Repo: unraid/api PR: 1181
File: web/store/theme.ts:0-0
Timestamp: 2025-02-24T14:51:21.328Z
Learning: In the Unraid API project's theme system, exact TypeScript type definitions are preferred over index signatures for theme variables to ensure better type safety.
Applied to files:
api/src/unraid-api/plugin/plugin.service.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Focus tests on observable behavior and outcomes, not implementation details such as exact error messages
Applied to files:
api/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-09-12T01:36:59.838Z
Learnt from: CR
Repo: unraid/api PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-09-12T01:36:59.838Z
Learning: Applies to web/__test__/**/*.{test,spec}.{ts,tsx} : Test component behavior and output, not implementation details
Applied to files:
api/src/core/utils/__test__/safe-mode.test.ts
📚 Learning: 2025-08-11T15:10:28.150Z
Learnt from: CR
Repo: unraid/api PR: 0
File: .cursor/rules/web-testing-rules.mdc:0-0
Timestamp: 2025-08-11T15:10:28.150Z
Learning: Applies to {**/*.test.ts,**/__test__/{components,store}/**/*.ts} : Do not rely on Nuxt auto-imports in tests; import required Vue utilities explicitly in test files
Applied to files:
api/src/core/utils/__test__/safe-mode.test.ts
🧬 Code graph analysis (5)
api/src/store/services/__test__/state-file-loader.test.ts (2)
api/src/store/index.ts (1)
store(10-16)api/src/store/services/state-file-loader.ts (1)
loadStateFileSync(49-81)
api/src/core/utils/safe-mode.ts (2)
api/src/store/index.ts (1)
store(10-16)api/src/store/services/state-file-loader.ts (1)
loadStateFileSync(49-81)
api/src/store/services/state-file-loader.ts (3)
api/src/store/index.ts (1)
store(10-16)api/src/core/utils/misc/parse-config.ts (1)
parseConfig(146-203)api/src/store/modules/emhttp.ts (2)
updateEmhttpState(182-192)SliceState(21-32)
api/src/unraid-api/plugin/plugin.service.ts (1)
api/src/core/utils/safe-mode.ts (1)
isSafeModeEnabled(5-17)
api/src/core/utils/__test__/safe-mode.test.ts (2)
api/src/store/index.ts (1)
store(10-16)api/src/core/utils/safe-mode.ts (1)
isSafeModeEnabled(5-17)
🔇 Additional comments (10)
api/src/store/modules/emhttp.ts (2)
166-176: LGTM! Clean mapping of state file keys to slice fields.The
stateFieldKeyMapcorrectly translatesStateFileKeyenum values to their correspondingSliceStatefield names, enabling the synchronous state loader to dispatch updates using the file-based keys.
190-191: LGTM! Reducer correctly applies the field mapping.The updated reducer uses the mapping to determine the target field, with a sensible fallback for unmapped keys. This enables the state-file-loader to update the store using
StateFileKeyvalues.api/src/core/utils/safe-mode.ts (1)
5-17: LGTM! Clean safe-mode detection with sensible fallbacks.The function correctly checks in-memory store state first, falls back to synchronous file loading when needed, and defaults to
falseif neither source is available. The type guard on line 7 prevents treating non-boolean values as safe-mode flags.api/src/unraid-api/plugin/plugin.service.ts (1)
24-33: LGTM! Safe-mode check properly gates plugin loading.The updated lazy initialization correctly checks safe mode before loading plugins, logging an informative warning and returning an empty array when safe mode is active. This preserves the singleton pattern while respecting OS-level safe mode.
api/src/core/utils/__test__/safe-mode.test.ts (1)
7-66: LGTM! Comprehensive test coverage for safe-mode detection.The test suite properly validates all three code paths: store state present, fallback to file loader, and default false. Mock usage follows best practices with proper restoration in
afterEach, and the type casting is acceptable for simulating edge cases in tests.api/src/unraid-api/plugin/__test__/plugin.service.test.ts (1)
13-46: LGTM! Tests properly verify safe-mode behavior.The test suite correctly validates that plugin loading is skipped when safe mode is enabled and proceeds normally when disabled. Mock usage and state reset between tests follows best practices.
api/src/store/services/__test__/state-file-loader.test.ts (2)
11-16: LGTM! Clean fixture management.The fixture loading and helper function provide a flexible way to test different safe-mode values while maintaining realistic test data.
18-81: LGTM! Comprehensive test coverage with proper cleanup.The test suite validates all critical paths: successful loading with store updates, graceful handling of missing configuration, and missing files. Temporary directory management with cleanup ensures test isolation.
api/src/store/services/state-file-loader.ts (2)
19-41: LGTM! Well-typed parser infrastructure.The
ParserReturnMapandPARSER_MAPprovide strong typing for the synchronous loader while avoiding dynamic imports. This is appropriate for bootstrap contexts where async loading is impractical.
49-81: LGTM! Synchronous loader correctly handles bootstrap constraints.The function appropriately handles missing configuration by returning
null, dispatches store updates as documented, and returns the parsed state. The type casting on line 68 is necessary due to TypeScript's limitations with complex generic constraints.
elibosley
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work on this!
Summary by CodeRabbit
Release Notes
New Features
Tests