Publish Container / build (push) Has been cancelled
- Community posts, replies, likes and lesson comments are refused (403) for finished and planned studies - Reading a closed study's community/comments returns an empty list with closed:true - Discussion hub lists only active studies; community page shows a closed notice - Lesson page hides Lesson Discussion, 'Discuss this lesson' and 'Go to Community' for inactive studies; Community buttons hidden on study/dashboard pages - Tests for isStudyDiscussionOpen Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
81 lines
3.3 KiB
JavaScript
81 lines
3.3 KiB
JavaScript
import { test } from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { state } from '../server/state.js'
|
|
import {
|
|
hasStudyAccess,
|
|
isStudyUserCompleted,
|
|
getAccessibleStudySlugs,
|
|
markStudyCompletedIfDone,
|
|
scheduleReengagementEmails,
|
|
} from '../server/study-helpers.js'
|
|
|
|
function setup() {
|
|
state.cachedSiteContent = {
|
|
studies: [{ slug: 'mini', sections: [{ id: 'a' }, { id: 'b' }] }, { slug: 'other', sections: [{ id: 'x' }] }],
|
|
}
|
|
}
|
|
|
|
test('completing every lesson marks the study completed and keeps access after leaving', () => {
|
|
setup()
|
|
const user = { id: 'u1', enrolledStudySlugs: ['mini'] }
|
|
assert.equal(markStudyCompletedIfDone(user, 'mini', ['a']), false)
|
|
assert.equal(markStudyCompletedIfDone(user, 'mini', ['a', 'b']), true)
|
|
assert.ok(isStudyUserCompleted(user, 'mini'))
|
|
|
|
user.enrolledStudySlugs = [] // student leaves
|
|
assert.ok(hasStudyAccess(user, 'mini'))
|
|
assert.ok(!hasStudyAccess(user, 'other'))
|
|
assert.deepEqual(getAccessibleStudySlugs(user), ['mini'])
|
|
})
|
|
|
|
test('completion is sticky when a lesson is added later, and not marked twice', () => {
|
|
setup()
|
|
const user = { id: 'u2', enrolledStudySlugs: ['mini'] }
|
|
markStudyCompletedIfDone(user, 'mini', ['a', 'b'])
|
|
state.cachedSiteContent.studies[0].sections.push({ id: 'c' })
|
|
assert.equal(markStudyCompletedIfDone(user, 'mini', ['a', 'b']), false)
|
|
assert.ok(isStudyUserCompleted(user, 'mini'))
|
|
assert.deepEqual(user.completedStudySlugs, ['mini'])
|
|
})
|
|
|
|
test('unknown or empty studies are never marked completed', () => {
|
|
setup()
|
|
const user = { id: 'u3', enrolledStudySlugs: [] }
|
|
assert.equal(markStudyCompletedIfDone(user, 'nope', ['a']), false)
|
|
state.cachedSiteContent.studies.push({ slug: 'empty', sections: [] })
|
|
assert.equal(markStudyCompletedIfDone(user, 'empty', []), false)
|
|
})
|
|
|
|
test('re-engagement emails skip students whose only study is finished', async () => {
|
|
setup()
|
|
const sent = []
|
|
state.studyUsers = [{
|
|
id: 'u4', username: 'a@b.co', studyRemindersEnabled: true, lastLoginAt: new Date(Date.now() - 40 * 864e5).toISOString(),
|
|
enrolledStudySlugs: ['mini'], completedStudySlugs: ['mini'], reengagementSentAt: {},
|
|
}, {
|
|
id: 'u5', username: 'c@d.co', studyRemindersEnabled: true, lastLoginAt: new Date(Date.now() - 40 * 864e5).toISOString(),
|
|
enrolledStudySlugs: ['mini'], reengagementSentAt: {},
|
|
}]
|
|
await scheduleReengagementEmails(async (email) => { sent.push(email) })
|
|
assert.deepEqual(sent, ['c@d.co'])
|
|
})
|
|
|
|
test('discussion is open only for active studies', async () => {
|
|
const { isStudyDiscussionOpen } = await import('../server/study-helpers.js')
|
|
const past = '2020-01-01T00:00:00Z'
|
|
const future = '2099-01-01T00:00:00Z'
|
|
state.cachedSiteContent = {
|
|
studies: [
|
|
{ slug: 'live', status: 'active', sections: [{ id: 'a', releasedAt: past }, { id: 'b', releasedAt: future }] },
|
|
{ slug: 'done', status: 'complete', sections: [{ id: 'a', releasedAt: past }] },
|
|
{ slug: 'auto-done', sections: [{ id: 'a', releasedAt: past }] }, // every lesson released => complete
|
|
{ slug: 'soon', status: 'planned', sections: [] },
|
|
],
|
|
}
|
|
assert.equal(isStudyDiscussionOpen('live'), true)
|
|
assert.equal(isStudyDiscussionOpen('done'), false)
|
|
assert.equal(isStudyDiscussionOpen('auto-done'), false)
|
|
assert.equal(isStudyDiscussionOpen('soon'), false)
|
|
assert.equal(isStudyDiscussionOpen('missing'), false)
|
|
})
|