musora-content-services 2.117.8 → 2.118.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +16 -0
- package/.coderabbit.yaml +0 -0
- package/.editorconfig +0 -0
- package/.github/pull_request_template.md +0 -0
- package/.github/workflows/conventional-commits.yaml +0 -0
- package/.github/workflows/docs.js.yml +0 -0
- package/.github/workflows/node.js.yml +0 -0
- package/.prettierignore +0 -0
- package/.prettierrc +0 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +45 -0
- package/CLAUDE.md +0 -0
- package/README.md +0 -0
- package/babel.config.cjs +0 -0
- package/check_content.js +30 -0
- package/check_content.mjs +32 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/constants/award-assets.js +0 -0
- package/src/constants/membership-permissions.ts +0 -0
- package/src/contentMetaData.js +9 -5
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/infrastructure/http/HttpClient.ts +0 -0
- package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
- package/src/infrastructure/http/index.ts +0 -0
- package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
- package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
- package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
- package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
- package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
- package/src/lib/ads/monoid.ts +0 -0
- package/src/lib/ads/semigroup.ts +0 -0
- package/src/lib/brands.ts +0 -0
- package/src/lib/lastUpdated.js +0 -0
- package/src/lib/sanity/filter.ts +0 -0
- package/src/lib/sanity/query.ts +0 -0
- package/src/services/api/types.js +0 -0
- package/src/services/api/types.ts +0 -0
- package/src/services/awards/award-callbacks.js +0 -0
- package/src/services/awards/award-query.js +0 -0
- package/src/services/awards/internal/.indexignore +0 -0
- package/src/services/awards/internal/award-definitions.js +0 -0
- package/src/services/awards/internal/award-events.js +0 -0
- package/src/services/awards/internal/award-manager.js +0 -0
- package/src/services/awards/internal/certificate-builder.js +0 -0
- package/src/services/awards/internal/completion-data-generator.js +0 -0
- package/src/services/awards/internal/content-progress-observer.js +0 -0
- package/src/services/awards/internal/image-utils.js +0 -0
- package/src/services/awards/internal/message-generator.js +0 -0
- package/src/services/awards/internal/types.js +0 -0
- package/src/services/awards/types.d.ts +0 -0
- package/src/services/awards/types.js +0 -0
- package/src/services/content/artist.ts +0 -0
- package/src/services/content/content.ts +0 -0
- package/src/services/content/genre.ts +0 -0
- package/src/services/content/instructor.ts +0 -0
- package/src/services/content-org/content-org.js +0 -0
- package/src/services/content-org/guided-courses.ts +0 -0
- package/src/services/content-org/learning-paths.ts +0 -0
- package/src/services/content-org/playlists-types.js +0 -0
- package/src/services/content-org/playlists.js +0 -0
- package/src/services/content.js +54 -15
- package/src/services/contentAggregator.js +0 -0
- package/src/services/contentLikes.js +0 -0
- package/src/services/contentProgress.js +0 -0
- package/src/services/dataContext.js +41 -0
- package/src/services/dateUtils.js +0 -0
- package/src/services/eventsAPI.js +0 -0
- package/src/services/forums/categories.ts +0 -0
- package/src/services/forums/forums.ts +0 -0
- package/src/services/forums/posts.ts +0 -0
- package/src/services/forums/threads.ts +0 -0
- package/src/services/forums/types.ts +0 -0
- package/src/services/gamification/awards.ts +0 -0
- package/src/services/gamification/gamification.js +0 -0
- package/src/services/imageSRCBuilder.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/liveTesting.ts +0 -0
- package/src/services/permissions/PermissionsAdapter.ts +0 -0
- package/src/services/permissions/PermissionsAdapterFactory.ts +0 -0
- package/src/services/permissions/PermissionsV1Adapter.ts +0 -0
- package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
- package/src/services/permissions/README.md +0 -0
- package/src/services/permissions/index.ts +0 -0
- package/src/services/progress-events.js +0 -0
- package/src/services/progress-row/base.js +13 -3
- package/src/services/progress-row/rows/.indexignore +0 -0
- package/src/services/progress-row/rows/content-card.js +0 -0
- package/src/services/progress-row/rows/playlist-card.js +0 -0
- package/src/services/railcontent.js +0 -0
- package/src/services/recommendations.js +5 -2
- package/src/services/reporting/README.md +0 -0
- package/src/services/reporting/reporting.ts +0 -0
- package/src/services/reporting/types.ts +0 -0
- package/src/services/sentry/.indexignore +0 -0
- package/src/services/sentry/index.ts +0 -0
- package/src/services/sync/.indexignore +0 -0
- package/src/services/sync/adapters/factory.ts +0 -0
- package/src/services/sync/adapters/lokijs.ts +0 -0
- package/src/services/sync/adapters/sqlite.ts +0 -0
- package/src/services/sync/context/index.ts +0 -0
- package/src/services/sync/context/providers/base.ts +0 -0
- package/src/services/sync/context/providers/connectivity.ts +0 -0
- package/src/services/sync/context/providers/durability.ts +0 -0
- package/src/services/sync/context/providers/index.ts +0 -0
- package/src/services/sync/context/providers/session.ts +0 -0
- package/src/services/sync/context/providers/tabs.ts +0 -0
- package/src/services/sync/context/providers/visibility.ts +0 -0
- package/src/services/sync/database/factory.ts +0 -0
- package/src/services/sync/effects/index.ts +0 -0
- package/src/services/sync/effects/logout-warning.ts +0 -0
- package/src/services/sync/errors/boundary.ts +0 -0
- package/src/services/sync/errors/index.ts +0 -0
- package/src/services/sync/errors/validators.ts +0 -0
- package/src/services/sync/fetch.ts +0 -0
- package/src/services/sync/index.ts +0 -0
- package/src/services/sync/manager.ts +0 -0
- package/src/services/sync/models/Base.ts +0 -0
- package/src/services/sync/models/ContentLike.ts +0 -0
- package/src/services/sync/models/ContentProgress.ts +0 -0
- package/src/services/sync/models/Practice.ts +0 -0
- package/src/services/sync/models/PracticeDayNote.ts +0 -0
- package/src/services/sync/models/UserAwardProgress.ts +0 -0
- package/src/services/sync/models/index.ts +0 -0
- package/src/services/sync/repositories/base.ts +0 -0
- package/src/services/sync/repositories/content-likes.ts +0 -0
- package/src/services/sync/repositories/content-progress.ts +0 -0
- package/src/services/sync/repositories/index.ts +0 -0
- package/src/services/sync/repositories/practice-day-notes.ts +0 -0
- package/src/services/sync/repositories/practices.ts +0 -0
- package/src/services/sync/repositories/user-award-progress.ts +0 -0
- package/src/services/sync/repository-proxy.ts +0 -0
- package/src/services/sync/resolver.ts +0 -0
- package/src/services/sync/retry.ts +0 -0
- package/src/services/sync/run-scope.ts +0 -0
- package/src/services/sync/schema/index.ts +0 -0
- package/src/services/sync/serializers/index.ts +0 -0
- package/src/services/sync/serializers/model.ts +0 -0
- package/src/services/sync/serializers/raw.ts +0 -0
- package/src/services/sync/store/index.ts +0 -0
- package/src/services/sync/store/push-coalescer.ts +0 -0
- package/src/services/sync/store-configs.ts +0 -0
- package/src/services/sync/strategies/base.ts +0 -0
- package/src/services/sync/strategies/index.ts +0 -0
- package/src/services/sync/strategies/initial.ts +0 -0
- package/src/services/sync/strategies/polling.ts +0 -0
- package/src/services/sync/telemetry/flood-prevention.ts +0 -0
- package/src/services/sync/telemetry/index.ts +0 -0
- package/src/services/sync/telemetry/sampling.ts +0 -0
- package/src/services/sync/utils/event-emitter.ts +0 -0
- package/src/services/sync/utils/index.ts +0 -0
- package/src/services/sync/utils/throttle.ts +0 -0
- package/src/services/sync/utils/timers.ts +0 -0
- package/src/services/urlBuilder.ts +0 -0
- package/src/services/user/account.ts +0 -0
- package/src/services/user/chat.js +0 -0
- package/src/services/user/interests.js +0 -0
- package/src/services/user/management.js +0 -0
- package/src/services/user/memberships.ts +0 -0
- package/src/services/user/notifications.js +0 -0
- package/src/services/user/onboarding.ts +0 -0
- package/src/services/user/payments.ts +0 -0
- package/src/services/user/permissions.js +0 -0
- package/src/services/user/profile.js +0 -0
- package/src/services/user/sessions.js +9 -2
- package/src/services/user/types.d.ts +0 -0
- package/src/services/user/types.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +0 -0
- package/test/HttpClient.test.js +0 -0
- package/test/awards/award-alacarte-observer.test.js +0 -0
- package/test/awards/award-auto-refresh.test.js +0 -0
- package/test/awards/award-calculations.test.js +0 -0
- package/test/awards/award-certificate-display.test.js +0 -0
- package/test/awards/award-collection-edge-cases.test.js +0 -0
- package/test/awards/award-collection-filtering.test.js +0 -0
- package/test/awards/award-completion-flow.test.js +0 -0
- package/test/awards/award-exclusion-handling.test.js +0 -0
- package/test/awards/award-multi-lesson.test.js +0 -0
- package/test/awards/award-observer-integration.test.js +0 -0
- package/test/awards/award-query-messages.test.js +0 -0
- package/test/awards/award-user-collection.test.js +0 -0
- package/test/awards/duplicate-prevention.test.js +0 -0
- package/test/awards/helpers/completion-mock.js +0 -0
- package/test/awards/helpers/index.js +0 -0
- package/test/awards/helpers/mock-setup.js +0 -0
- package/test/awards/helpers/progress-emitter.js +0 -0
- package/test/awards/message-generator.test.js +0 -0
- package/test/content.test.js +0 -0
- package/test/contentLikes.test.js +0 -0
- package/test/contentProgress.test.js +0 -0
- package/test/dataContext.test.js +0 -0
- package/test/forum.test.js +0 -0
- package/test/imageSRCBuilder.test.js +0 -0
- package/test/imageSRCVerify.test.js +0 -0
- package/test/initializeTests.js +0 -0
- package/test/learningPaths.test.js +0 -0
- package/test/lib/__snapshots__/filter.test.ts.snap +0 -0
- package/test/lib/filter.test.ts +0 -0
- package/test/lib/lastUpdated.test.js +0 -0
- package/test/lib/query.test.ts +0 -0
- package/test/live/contentProgressLive.test.js +0 -0
- package/test/live/railcontentLive.test.js +0 -0
- package/test/log.js +0 -0
- package/test/logout.test.js +199 -0
- package/test/mockData/award-definitions.js +0 -0
- package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
- package/test/mockData/mockData_progress_content.json +0 -0
- package/test/mockData/mockData_sanity_progress_content.json +0 -0
- package/test/mockData/mockData_user_practices.json +0 -0
- package/test/notifications.test.js +0 -0
- package/test/progressRows.test.js +0 -0
- package/test/reporting.test.js +132 -0
- package/test/sanityQueryService.test.js +0 -0
- package/test/streakMessage.test.js +0 -0
- package/test/sync/adapter.ts +0 -0
- package/test/sync/initialize-sync-manager.js +0 -0
- package/test/sync/models/award-database-integration.test.js +0 -0
- package/test/user/permissions.test.js +0 -0
- package/test/userActivity.test.js +0 -0
- package/test_owned_navigate.js +74 -0
- package/tools/generate-index.cjs +0 -0
- package/tsconfig.json +17 -0
|
@@ -126,9 +126,12 @@ export async function rankItems(brand, content_ids) {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
export async function recommendations(brand, { section = '' } = {}) {
|
|
129
|
+
export async function recommendations(brand, { section = '', contentTypes = [] } = {}) {
|
|
130
130
|
section = section.toUpperCase().replace('-', '_')
|
|
131
131
|
const sectionString = section ? `§ion=${section}` : ''
|
|
132
|
-
const
|
|
132
|
+
const contentTypesString = contentTypes.length > 0
|
|
133
|
+
? contentTypes.map(type => `&content_types[]=${encodeURIComponent(type)}`).join('')
|
|
134
|
+
: ''
|
|
135
|
+
const url = `/api/content/v1/recommendations?brand=${brand}${sectionString}${contentTypesString}`
|
|
133
136
|
return await GET(url)
|
|
134
137
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @module Sessions
|
|
3
3
|
*/
|
|
4
4
|
import { globalConfig } from '../config.js'
|
|
5
|
+
import { clearAllCachedData } from '../dataContext.js'
|
|
5
6
|
import { USER_PIN_PROGRESS_KEY } from '../progress-row/base.js'
|
|
6
7
|
import './types.js'
|
|
7
8
|
|
|
@@ -50,9 +51,11 @@ export async function login(email, password, deviceName, deviceToken, platform)
|
|
|
50
51
|
|
|
51
52
|
// TODO: refactor this. I don't think this is the place for it but we need it fixed for the system test
|
|
52
53
|
if (res.ok) {
|
|
54
|
+
const userId = data.user?.id
|
|
55
|
+
const userPinKey = userId ? `user_pin_progress_row_${userId}` : USER_PIN_PROGRESS_KEY
|
|
53
56
|
await globalConfig.localStorage.setItem(
|
|
54
|
-
|
|
55
|
-
JSON.stringify(data.
|
|
57
|
+
userPinKey,
|
|
58
|
+
JSON.stringify(data.user?.brand_pinned_progress || {})
|
|
56
59
|
)
|
|
57
60
|
}
|
|
58
61
|
|
|
@@ -91,6 +94,7 @@ export async function login(email, password, deviceName, deviceToken, platform)
|
|
|
91
94
|
|
|
92
95
|
/**
|
|
93
96
|
* Logs the user out of the current session.
|
|
97
|
+
* Clears all cached data to prevent data leakage between users.
|
|
94
98
|
*
|
|
95
99
|
* @returns {Promise<void>}
|
|
96
100
|
*
|
|
@@ -108,6 +112,9 @@ export async function logout() {
|
|
|
108
112
|
'Content-Type': 'application/json',
|
|
109
113
|
},
|
|
110
114
|
})
|
|
115
|
+
|
|
116
|
+
// Clear all locally cached data to prevent data leakage between users
|
|
117
|
+
await clearAllCachedData()
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
/**
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/HttpClient.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/content.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/dataContext.test.js
CHANGED
|
File without changes
|
package/test/forum.test.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/initializeTests.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/lib/filter.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/test/lib/query.test.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/test/log.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { initializeService, globalConfig } from '../src/services/config.js'
|
|
2
|
+
import { LocalStorageMock } from './localStorageMock'
|
|
3
|
+
import { logout } from '../src/services/user/sessions.js'
|
|
4
|
+
import { clearAllCachedData } from '../src/services/dataContext.js'
|
|
5
|
+
import { USER_PIN_PROGRESS_KEY } from '../src/services/progress-row/base.js'
|
|
6
|
+
|
|
7
|
+
describe('logout cache clearing', function () {
|
|
8
|
+
let mockLocalStorage
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
// Create fresh localStorage for each test
|
|
12
|
+
mockLocalStorage = new LocalStorageMock()
|
|
13
|
+
|
|
14
|
+
// Simple initialization without SyncManager
|
|
15
|
+
const config = {
|
|
16
|
+
sanityConfig: {
|
|
17
|
+
token: 'test-token',
|
|
18
|
+
projectId: 'test-project',
|
|
19
|
+
dataset: 'test',
|
|
20
|
+
useCachedAPI: true,
|
|
21
|
+
version: '2021-06-07',
|
|
22
|
+
debug: false,
|
|
23
|
+
useDummyRailContentMethods: true,
|
|
24
|
+
},
|
|
25
|
+
railcontentConfig: {
|
|
26
|
+
baseUrl: 'https://test.musora.com',
|
|
27
|
+
token: 'test-token',
|
|
28
|
+
userId: 123,
|
|
29
|
+
authToken: 'test-token',
|
|
30
|
+
},
|
|
31
|
+
sessionConfig: { token: 'test-token', userId: 123, authToken: 'test-token' },
|
|
32
|
+
baseUrl: 'https://test.musora.com',
|
|
33
|
+
localStorage: mockLocalStorage,
|
|
34
|
+
isMA: false,
|
|
35
|
+
}
|
|
36
|
+
initializeService(config)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
jest.restoreAllMocks()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('clearAllCachedData does NOT remove user_pin_progress_row keys', async () => {
|
|
44
|
+
const localStorage = globalConfig.localStorage
|
|
45
|
+
const userId = 123
|
|
46
|
+
const userPinKey = `user_pin_progress_row_${userId}`
|
|
47
|
+
|
|
48
|
+
// Add pinned progress data for user 123
|
|
49
|
+
localStorage.setItem(userPinKey, JSON.stringify({
|
|
50
|
+
drumeo: { id: 123, progressType: 'content', pinnedAt: '2025-01-01' },
|
|
51
|
+
pianote: { id: 456, progressType: 'playlist', pinnedAt: '2025-01-02' }
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
// Add pinned progress data for another user
|
|
55
|
+
localStorage.setItem('user_pin_progress_row_456', JSON.stringify({
|
|
56
|
+
drumeo: { id: 789, progressType: 'content' }
|
|
57
|
+
}))
|
|
58
|
+
|
|
59
|
+
// Verify data exists
|
|
60
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
61
|
+
expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
|
|
62
|
+
|
|
63
|
+
// Clear cached data
|
|
64
|
+
await clearAllCachedData()
|
|
65
|
+
|
|
66
|
+
// Verify user pin progress rows are NOT removed
|
|
67
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
68
|
+
expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('clearAllCachedData removes dataContext keys', async () => {
|
|
72
|
+
const localStorage = globalConfig.localStorage
|
|
73
|
+
|
|
74
|
+
// Add some dataContext keys
|
|
75
|
+
localStorage.setItem('dataContext_test1', JSON.stringify({ data: 'test1' }))
|
|
76
|
+
localStorage.setItem('dataContext_test2', JSON.stringify({ data: 'test2' }))
|
|
77
|
+
localStorage.setItem('other_key', 'should_remain')
|
|
78
|
+
|
|
79
|
+
// Verify data exists
|
|
80
|
+
expect(localStorage.getItem('dataContext_test1')).not.toBeNull()
|
|
81
|
+
expect(localStorage.getItem('dataContext_test2')).not.toBeNull()
|
|
82
|
+
expect(localStorage.getItem('other_key')).not.toBeNull()
|
|
83
|
+
|
|
84
|
+
// Clear all cached data
|
|
85
|
+
await clearAllCachedData()
|
|
86
|
+
|
|
87
|
+
// Verify dataContext keys were removed but other key remains
|
|
88
|
+
expect(localStorage.getItem('dataContext_test1')).toBeNull()
|
|
89
|
+
expect(localStorage.getItem('dataContext_test2')).toBeNull()
|
|
90
|
+
expect(localStorage.getItem('other_key')).toBe('should_remain')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('clearAllCachedData removes ONLY dataContext keys', async () => {
|
|
94
|
+
const localStorage = globalConfig.localStorage
|
|
95
|
+
const userId = 123
|
|
96
|
+
const userPinKey = `user_pin_progress_row_${userId}`
|
|
97
|
+
|
|
98
|
+
// Add various cached data
|
|
99
|
+
localStorage.setItem(userPinKey, JSON.stringify({ drumeo: { id: 123 } }))
|
|
100
|
+
localStorage.setItem('dataContext_progress', JSON.stringify({ data: 'progress' }))
|
|
101
|
+
localStorage.setItem('dataContext_likes', JSON.stringify({ data: 'likes' }))
|
|
102
|
+
localStorage.setItem('unrelated_key', 'preserved')
|
|
103
|
+
localStorage.setItem('user_pin_progress_row_999', JSON.stringify({ drumeo: { id: 999 } }))
|
|
104
|
+
|
|
105
|
+
// Verify all data exists
|
|
106
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
107
|
+
expect(localStorage.getItem('dataContext_progress')).not.toBeNull()
|
|
108
|
+
expect(localStorage.getItem('dataContext_likes')).not.toBeNull()
|
|
109
|
+
expect(localStorage.getItem('unrelated_key')).not.toBeNull()
|
|
110
|
+
|
|
111
|
+
// Clear cached data
|
|
112
|
+
await clearAllCachedData()
|
|
113
|
+
|
|
114
|
+
// Verify only dataContext keys were cleared
|
|
115
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
116
|
+
expect(localStorage.getItem('dataContext_progress')).toBeNull()
|
|
117
|
+
expect(localStorage.getItem('dataContext_likes')).toBeNull()
|
|
118
|
+
expect(localStorage.getItem('unrelated_key')).toBe('preserved')
|
|
119
|
+
// All user pinned data should remain
|
|
120
|
+
expect(localStorage.getItem('user_pin_progress_row_999')).not.toBeNull()
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('logout calls clearAllCachedData', async () => {
|
|
124
|
+
// Mock fetch to prevent actual API call
|
|
125
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
126
|
+
ok: true,
|
|
127
|
+
json: async () => ({ success: true })
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
const localStorage = globalConfig.localStorage
|
|
131
|
+
const userId = 123
|
|
132
|
+
const userPinKey = `user_pin_progress_row_${userId}`
|
|
133
|
+
|
|
134
|
+
// Add cached data
|
|
135
|
+
localStorage.setItem(userPinKey, JSON.stringify({ drumeo: { id: 123 } }))
|
|
136
|
+
localStorage.setItem('dataContext_test', JSON.stringify({ data: 'test' }))
|
|
137
|
+
localStorage.setItem('user_pin_progress_row_456', JSON.stringify({ drumeo: { id: 456 } }))
|
|
138
|
+
|
|
139
|
+
// Verify data exists before logout
|
|
140
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
141
|
+
expect(localStorage.getItem('dataContext_test')).not.toBeNull()
|
|
142
|
+
|
|
143
|
+
// Call logout
|
|
144
|
+
await logout()
|
|
145
|
+
|
|
146
|
+
// Verify only dataContext was cleared, pinned progress remains
|
|
147
|
+
expect(localStorage.getItem(userPinKey)).not.toBeNull()
|
|
148
|
+
expect(localStorage.getItem('dataContext_test')).toBeNull()
|
|
149
|
+
expect(localStorage.getItem('user_pin_progress_row_456')).not.toBeNull()
|
|
150
|
+
|
|
151
|
+
// Verify fetch was called (logout API endpoint)
|
|
152
|
+
expect(global.fetch).toHaveBeenCalledWith(
|
|
153
|
+
expect.stringContaining('/api/user-management-system/v1/sessions'),
|
|
154
|
+
expect.objectContaining({
|
|
155
|
+
method: 'DELETE'
|
|
156
|
+
})
|
|
157
|
+
)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('logout prevents cross-account data leakage', async () => {
|
|
161
|
+
// Mock fetch to prevent actual API call
|
|
162
|
+
global.fetch = jest.fn().mockResolvedValue({
|
|
163
|
+
ok: true,
|
|
164
|
+
json: async () => ({ success: true })
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const localStorage = globalConfig.localStorage
|
|
168
|
+
const user1Id = 123
|
|
169
|
+
const user2Id = 456
|
|
170
|
+
const user1PinKey = `user_pin_progress_row_${user1Id}`
|
|
171
|
+
const user2PinKey = `user_pin_progress_row_${user2Id}`
|
|
172
|
+
|
|
173
|
+
// Simulate user 1 (id: 123) with pinned content and dataContext
|
|
174
|
+
localStorage.setItem(user1PinKey, JSON.stringify({
|
|
175
|
+
drumeo: { id: 12345, progressType: 'content', pinnedAt: '2025-01-14' }
|
|
176
|
+
}))
|
|
177
|
+
localStorage.setItem('dataContext_progress', JSON.stringify({ userId: 1, data: 'user1' }))
|
|
178
|
+
|
|
179
|
+
// Simulate user 2 (id: 456) already has pinned content
|
|
180
|
+
localStorage.setItem(user2PinKey, JSON.stringify({
|
|
181
|
+
drumeo: { id: 67890, progressType: 'content', pinnedAt: '2025-01-13' }
|
|
182
|
+
}))
|
|
183
|
+
|
|
184
|
+
// User 1 logs out
|
|
185
|
+
await logout()
|
|
186
|
+
|
|
187
|
+
// Verify dataContext is cleared (prevents cross-account leakage)
|
|
188
|
+
expect(localStorage.getItem('dataContext_progress')).toBeNull()
|
|
189
|
+
|
|
190
|
+
// Verify user 1's pinned progress remains (user-specific key)
|
|
191
|
+
expect(localStorage.getItem(user1PinKey)).not.toBeNull()
|
|
192
|
+
|
|
193
|
+
// Verify user 2's pinned content is NOT affected by user 1's logout
|
|
194
|
+
const user2PinnedData = localStorage.getItem(user2PinKey)
|
|
195
|
+
expect(user2PinnedData).not.toBeNull()
|
|
196
|
+
const user2Data = JSON.parse(user2PinnedData)
|
|
197
|
+
expect(user2Data.drumeo.id).toBe(67890)
|
|
198
|
+
})
|
|
199
|
+
})
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|