From 13201bca61794ec9a98b5c96db3fba7ed027e2cc Mon Sep 17 00:00:00 2001 From: vreshch Date: Tue, 11 Aug 2026 23:12:55 +0200 Subject: [PATCH] test(blog): assert scheduling behaviour, not one article's timestamp Master CI went red because a scheduled post published. Two tests were pinned to a single article's publishAt: - 'excludes a post scheduled for future publishAt' expected everything-is-code to be missing from the listing. Its publishAt passed today, so the post correctly appeared and the test failed. A scheduled article going live is the system working; it should never look like a broken build. - 'returns a scheduled post directly by slug' asserted the literal string '2026-08-10T08:00:00Z', so rescheduling the article broke CI - which says nothing about getPost, the function under test. Both now assert behaviour. The listing test checks the INVARIANT across every post (nothing listed may still be hidden by the scheduler), which is strictly stronger than naming one slug and cannot rot. The lookup test checks that a direct slug lookup ignores scheduling, using a fixed past date. publishAt is deliberately absent from the listing type, so the invariant reads it from the full post - which is also the honest check. Verified locally with the exact CI sequence: type-check, lint, build, 30 tests, format:check. --- src/lib/blog.test.ts | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/lib/blog.test.ts b/src/lib/blog.test.ts index 5eb5aab..9442649 100644 --- a/src/lib/blog.test.ts +++ b/src/lib/blog.test.ts @@ -44,11 +44,20 @@ describe('lib/blog', () => { expect(post?.coverUrl).toBe(`/blog/${FIRST_POST_SLUG}/images/cover.png`); }); - it('excludes a post scheduled for future publishAt from the listing', async () => { + it('never lists a post whose publishAt is still in the future', async () => { + // Asserts the INVARIANT over every post rather than naming one article. The + // previous version pinned a specific slug it expected to be hidden, so it + // passed only until that article published - which made a scheduled post going + // live look like a broken build. + // The listing type deliberately omits publishAt, so read it from the full + // post - that is also the honest check: the index must not surface anything + // the scheduler would still hide. const posts = await getAllPosts(); - const post = posts.find((p) => p.slug === 'everything-is-code'); - // Placeholder publishAt; PR body flags it must be set to the real Medium schedule before merge. - expect(post).toBeUndefined(); + const now = new Date(); + for (const meta of posts) { + const full = await getPost(meta.slug); + expect(isPostVisible(full?.publishAt, now)).toBe(true); + } }); }); @@ -70,7 +79,11 @@ describe('lib/blog', () => { it('returns a scheduled post directly by slug regardless of publishAt', async () => { const post = await getPost('everything-is-code'); expect(post).not.toBeNull(); - expect(post?.publishAt).toBe('2026-08-10T08:00:00Z'); + // Assert the BEHAVIOUR - a direct slug lookup ignores scheduling - not the + // literal timestamp. Pinning the exact publishAt made CI fail every time the + // post was rescheduled, which says nothing about getPost. + expect(post?.publishAt).toBeTruthy(); + expect(isPostVisible(post!.publishAt!, new Date('2020-01-01T00:00:00Z'))).toBe(false); }); });