Skip to content

Commit 763cf37

Browse files
vershwalcursoragent
andcommitted
Added integration coverage for the lazy find-resource hook
ref https://linear.app/ghost/issue/HKG-1817 - the unit tests stub the database, so nothing proved the model queries the hook depends on; this exercises the hook against real models so a change to the published-only scoping, the post/page split, or the TagPublic/Author shouldHavePosts gate fails loudly - pins the security-relevant guards: a guessed slug must not surface a draft, a tag with no published posts, or a staff user, matching what the eager service would have hidden - asserts withRelated genuinely populates tags/authors, since that population is what lets the service re-check tag:/author: route filters rather than silently 404ing Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f29d44b commit 763cf37

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

ghost/core/test/integration/url-service.test.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ const assert = require('node:assert/strict');
22
const sinon = require('sinon');
33
const testUtils = require('../utils');
44
const configUtils = require('../utils/config-utils');
5+
const models = require('../../core/server/models');
56
const UrlService = require('../../core/server/services/url/url-service');
7+
const LazyUrlService = require('../../core/server/services/url/lazy-url-service');
8+
const {createFindResource} = require('../../core/server/services/url/lazy-find-resource');
69

710
describe('Integration: services/url/UrlService', function () {
811
let urlService;
@@ -108,6 +111,108 @@ describe('Integration: services/url/UrlService', function () {
108111
});
109112
});
110113

114+
describe('functional: lazy findResource against real models', function () {
115+
let findResource;
116+
117+
before(testUtils.teardownDb);
118+
before(testUtils.setup('users:roles', 'posts'));
119+
120+
before(function () {
121+
findResource = createFindResource(models);
122+
});
123+
124+
describe('posts', function () {
125+
it('finds a published post by slug and loads its tags/authors relations', async function () {
126+
const post = await findResource('posts', {slug: 'html-ipsum'});
127+
128+
assert.ok(post, 'expected the published post to be found');
129+
assert.equal(post.slug, 'html-ipsum');
130+
assert.equal(post.type, 'post');
131+
assert.ok(Array.isArray(post.tags), 'tags relation should be loaded');
132+
assert.ok(Array.isArray(post.authors), 'authors relation should be loaded');
133+
assert.ok(post.authors.length >= 1, 'posts always have at least one author');
134+
});
135+
136+
it('does not return draft posts', async function () {
137+
assert.equal(await findResource('posts', {slug: 'unfinished'}), null);
138+
});
139+
140+
it('does not return a page when querying the posts collection', async function () {
141+
assert.equal(await findResource('posts', {slug: 'static-page-test'}), null);
142+
});
143+
});
144+
145+
describe('pages', function () {
146+
it('finds a published page by slug', async function () {
147+
const page = await findResource('pages', {slug: 'static-page-test'});
148+
149+
assert.ok(page, 'expected the published page to be found');
150+
assert.equal(page.slug, 'static-page-test');
151+
assert.equal(page.type, 'page');
152+
});
153+
154+
it('does not return draft pages', async function () {
155+
assert.equal(await findResource('pages', {slug: 'static-page-draft'}), null);
156+
});
157+
158+
it('does not return a post when querying the pages collection', async function () {
159+
assert.equal(await findResource('pages', {slug: 'html-ipsum'}), null);
160+
});
161+
});
162+
163+
describe('tags', function () {
164+
it('finds a public tag that has published posts', async function () {
165+
const tag = await findResource('tags', {slug: 'kitchen-sink'});
166+
167+
assert.ok(tag, 'expected the public tag to be found');
168+
assert.equal(tag.slug, 'kitchen-sink');
169+
});
170+
171+
it('does not surface a tag with no published posts', async function () {
172+
const emptyTag = testUtils.DataGenerator.forKnex.tags[3];
173+
assert.equal(await findResource('tags', {slug: emptyTag.slug}), null);
174+
});
175+
});
176+
177+
describe('authors', function () {
178+
it('finds an author with published posts', async function () {
179+
const author = await findResource('authors', {slug: 'joe-bloggs'});
180+
181+
assert.ok(author, 'expected the author with posts to be found');
182+
assert.equal(author.slug, 'joe-bloggs');
183+
});
184+
185+
it('does not surface a user with no published posts', async function () {
186+
const noPosts = testUtils.DataGenerator.forKnex.users[1];
187+
assert.equal(await findResource('authors', {slug: noPosts.slug}), null);
188+
});
189+
});
190+
191+
it('returns null for unknown router types without querying', async function () {
192+
assert.equal(await findResource('unknown', {slug: 'html-ipsum'}), null);
193+
});
194+
195+
describe('end-to-end via LazyUrlService.resolveUrl', function () {
196+
it('resolves a real published post URL through the service', async function () {
197+
const service = new LazyUrlService({findResource});
198+
service.onRouterAddedType('posts', null, 'posts', '/:slug/');
199+
200+
const resource = await service.resolveUrl('/html-ipsum/');
201+
202+
assert.ok(resource);
203+
assert.equal(resource.type, 'posts');
204+
assert.equal(resource.slug, 'html-ipsum');
205+
});
206+
207+
it('returns null for a draft slug', async function () {
208+
const service = new LazyUrlService({findResource});
209+
service.onRouterAddedType('posts', null, 'posts', '/:slug/');
210+
211+
assert.equal(await service.resolveUrl('/unfinished/'), null);
212+
});
213+
});
214+
});
215+
111216
describe('functional: extended/modified routing set', function () {
112217
before(testUtils.teardownDb);
113218
before(testUtils.setup('users:roles', 'posts'));

0 commit comments

Comments
 (0)