Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 38 additions & 31 deletions nx.json
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"build": {
"dependsOn": ["^build"],
"cache": true
},
"lint": {
"cache": true
},
"test": {
"dependsOn": ["^build"],
"cache": true
}
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"build": {
"dependsOn": [
"^build"
],
"cache": true
},
"release": {
"projects": ["packages/*"],
"projectsRelationship": "independent",
"version": {
"preserveMatchingDependencyRanges": false,
"git": {
"commit": true,
"tag": true,
"commitMessage": "Published new versions"
}
},
"changelog": {
"workspaceChangelog": false
},
"publish": {
"registry": "https://registry.npmjs.org"
}
"lint": {
"cache": true
},
"test": {
"dependsOn": [
"^build"
],
"cache": true
}
},
"release": {
"projects": [
"packages/*"
],
"projectsRelationship": "independent",
"version": {
"preserveMatchingDependencyRanges": false,
"git": {
"commit": true,
"tag": true,
"commitMessage": "Published new versions"
}
},
"changelog": {
"workspaceChangelog": false
},
"publish": {
"registry": "https://registry.npmjs.org"
}
}
},
"analytics": true
}
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"presetup": "yarn",
"setup": "yarn",
"test": "nx run-many -t test --parallel=10 --outputStyle=dynamic-legacy",
"test:ci": "nx run-many -t test --outputStyle=dynamic",
"test:ci": "nx run-many -t test --outputStyle=static --nxBail",
"lint": "nx run-many -t lint --outputStyle=dynamic",
"preship": "git diff --quiet && git diff --cached --quiet || (echo 'Error: working tree must be clean before shipping' && exit 1) && yarn test",
"ship": "nx release version --git-push --git-remote ${GHOST_UPSTREAM:-origin}",
Expand All @@ -29,13 +29,13 @@
},
"devDependencies": {
"@nx/js": "22.6.1",
"@vitest/coverage-v8": "3.2.4",
"@vitest/coverage-v8": "4.1.0",
"eslint": "8.57.1",
"eslint-plugin-ghost": "3.5.0",
"nx": "22.6.1",
"sinon": "21.0.3",
"ts-node": "10.9.2",
"vitest": "3.2.4"
"vitest": "4.1.0"
},
"resolutions": {
"node-loggly-bulk": "^4.0.2"
Expand Down
52 changes: 52 additions & 0 deletions packages/bookshelf-pagination/test/pagination.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,58 @@ describe('@tryghost/bookshelf-pagination', function () {
assert.equal(modelState.rawCalls[0], 'count(distinct posts.id) as aggregate');
});

it('useSmartCount supports SQL fragments returned as an array', async function () {
const {bookshelf, modelState} = createBookshelf({countRows: [{aggregate: 1}]});
const model = new bookshelf.Model();

const originalQuery = model.query;
model.query = function () {
const qb = originalQuery.apply(this, arguments);
if (arguments.length === 0) {
qb.toSQL = () => [
{sql: 'select *'},
{},
{sql: 'from `posts`, `tags` where `posts`.`id` = `tags`.`post_id`'}
];
}
return qb;
};

await model.fetchPage({page: 1, limit: 10, useSmartCount: true});

assert.equal(modelState.rawCalls[0], 'count(distinct posts.id) as aggregate');
});

it('useSmartCount falls back safely when compiled SQL is missing', async function () {
const {bookshelf, modelState} = createBookshelf({countRows: [{aggregate: 1}]});
const model = new bookshelf.Model();

const originalQuery = model.query;
model.query = function () {
const qb = originalQuery.apply(this, arguments);
if (arguments.length === 0) {
qb.toSQL = () => ({});
}
return qb;
};

await model.fetchPage({page: 1, limit: 10, useSmartCount: true});

assert.equal(modelState.rawCalls[0], 'count(*) as aggregate');
});

it('handleRelation does not duplicate entries and ignores same-table properties', function () {
const {bookshelf} = createBookshelf();
const model = new bookshelf.Model();
model.eagerLoad = ['authors'];

paginationPlugin.paginationUtils.handleRelation(model, 'posts.id');
paginationPlugin.paginationUtils.handleRelation(model, 'title');
paginationPlugin.paginationUtils.handleRelation(model, 'authors.name');

assert.deepEqual(model.eagerLoad, ['authors']);
});

it('falls back to zero total when aggregate row is missing', async function () {
const {bookshelf} = createBookshelf({countRows: []});
const model = new bookshelf.Model();
Expand Down
6 changes: 1 addition & 5 deletions packages/config/lib/config.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
const getConfig = require('./get-config');

let config;
const config = getConfig();
function initConfig() {
if (!config) {
config = getConfig();
}

return config;
}

Expand Down
5 changes: 5 additions & 0 deletions packages/config/test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ describe('Config', function () {
assert.equal(config.get('env'), 'development');
});

it('handles missing process root gracefully', function () {
const config = withEnv('testing', () => withRoot('', () => loadFreshGetConfig()()));
assert.equal(config.get('env'), 'testing');
});

it('index exports lib/config and only initializes config once', function () {
const originalGetConfig = require(getConfigPath);
const fakeConfig = {name: 'fake-config'};
Expand Down
19 changes: 18 additions & 1 deletion packages/domain-events/lib/DomainEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class DomainEvents {
}
if (this.#trackingEnabled) {
this.#onProcessed();
} else {
// Tracking is disabled outside tests.
}
});
}
Expand All @@ -61,6 +63,8 @@ class DomainEvents {
static dispatchRaw(name, data) {
if (this.#trackingEnabled) {
this.#dispatchCount += DomainEvents.ee.listenerCount(name);
} else {
// Tracking is disabled outside tests.
}
DomainEvents.ee.emit(name, data);
}
Expand All @@ -80,8 +84,9 @@ class DomainEvents {
// Resolve immediately if there are no events in the queue
resolve();
return;
} else {
this.#awaitQueue.push({resolve});
}
this.#awaitQueue.push({resolve});
});
}

Expand All @@ -92,8 +97,20 @@ class DomainEvents {
item.resolve();
}
this.#awaitQueue = [];
} else {
// Wait for the remaining tracked listeners.
}
}

static setTrackingEnabledForTest(enabled) {
this.#trackingEnabled = enabled;
}

static resetTrackingStateForTest() {
this.#awaitQueue = [];
this.#dispatchCount = 0;
this.#processedCount = 0;
}
}

module.exports = DomainEvents;
52 changes: 52 additions & 0 deletions packages/domain-events/test/DomainEvents.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ describe('DomainEvents', function () {
afterEach(function () {
sinon.restore();
DomainEvents.ee.removeAllListeners();
DomainEvents.resetTrackingStateForTest();
DomainEvents.setTrackingEnabledForTest(process.env.NODE_ENV?.startsWith('test'));
});

it('Will call multiple subscribers with the event when it is dispatched', async function () {
Expand Down Expand Up @@ -77,6 +79,22 @@ describe('DomainEvents', function () {
assert.equal(stub.calledTwice, true);
});

it('works when tracking is disabled', async function () {
let handled = false;

DomainEvents.setTrackingEnabledForTest(false);

DomainEvents.subscribe(TestEvent, () => {
handled = true;
});

DomainEvents.dispatch(new TestEvent('No tracking'));
await sleep(0);
await DomainEvents.allSettled();

assert.equal(handled, true);
});

describe('allSettled', function () {
it('Resolves when there are no events', async function () {
await DomainEvents.allSettled();
Expand All @@ -98,5 +116,39 @@ describe('DomainEvents', function () {
await DomainEvents.allSettled();
assert.equal(counter, 2);
});

it('waits for every tracked listener before resolving', async function () {
let resolveFirst;
let resolveSecond;

DomainEvents.subscribe(TestEvent, () => {
return new Promise((resolve) => {
resolveFirst = resolve;
});
});
DomainEvents.subscribe(TestEvent, () => {
return new Promise((resolve) => {
resolveSecond = resolve;
});
});

DomainEvents.dispatch(new TestEvent('Hello, world!'));

let settled = false;
const allSettled = DomainEvents.allSettled().then(() => {
settled = true;
});

await sleep(0);
assert.equal(settled, false);

resolveFirst();
await sleep(0);
assert.equal(settled, false);

resolveSecond();
await allSettled;
assert.equal(settled, true);
});
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing

exports[`Example App Set & Expect check body using snapshot matching errors correctly for random data 1: [body] 1`] = `
Object {
Expand Down
3 changes: 3 additions & 0 deletions packages/express-test/test/utils/overrides.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const {snapshotManager} = require('@tryghost/jest-snapshot');
const SNAPSHOT_ROOT = '__jest_snapshots__';

/* eslint-disable ghost/mocha/no-mocha-arrows, ghost/mocha/no-top-level-hooks, ghost/mocha/handle-done-callback */
beforeAll(() => { // eslint-disable-line no-undef
// Keep custom jest-snapshot files out of Vitest's native __snapshots__ discovery.
snapshotManager.defaultSnapshotRoot = SNAPSHOT_ROOT;
snapshotManager.resetRegistry();
});

Expand Down
5 changes: 3 additions & 2 deletions packages/job-manager/test/job-manager.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,7 @@ describe('Job Manager', function () {
assert.equal(JobModel.edit.args[1][0].status, 'failed');

// simulate process restart and "fresh" slate to add the job
jobManager.removeJob('failed-oneoff');
await jobManager.removeJob('failed-oneoff').catch(() => {});
const completion2 = jobManager.awaitCompletion('failed-oneoff');

await jobManager.addOneOffJob({
Expand Down Expand Up @@ -664,7 +664,8 @@ describe('Job Manager', function () {
id: 'unique',
get: () => status
}),
add: sinon.stub().resolves()
add: sinon.stub().resolves(),
edit: sinon.stub().resolves()
};

jobManager = new JobManager({JobModel, config: stubConfig});
Expand Down
31 changes: 31 additions & 0 deletions packages/metrics/test/metrics.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,35 @@ describe('Logging', function () {
ghostMetrics.metric(name, value);
assert.equal(ElasticSearch.prototype.index.calledOnce, true);
});

it('ships object values with pre-set timestamp without adding metadata when disabled', async function () {
const ghostMetrics = new GhostMetrics({
metrics: {
transports: ['elasticsearch']
},
elasticsearch: {
host: 'https://test-elasticsearch',
username: 'user',
password: 'pass'
}
});

// Force metadata check false branch for coverage.
ghostMetrics.metadata = null;

const payload = {
value: 101,
'@timestamp': 12345
};

await new Promise((resolve) => {
sandbox.stub(ElasticSearch.prototype, 'index').callsFake(function (data, index) {
assert.deepEqual(data, payload);
assert.equal(index, 'metrics-object-metric');
resolve();
});

ghostMetrics.metric('object-metric', payload);
});
});
});
Loading
Loading