Issue changes are recorded per field in the issue_change table, in the same transaction as the issue itself. The new history module exposes: - GET /api/issues/:issueId/history — change timeline grouped by save - GET /api/issues/stats/daily — created, status transitions and status snapshot per day, with time zone support The migration backfills history for existing issues. Setting a field to its current value no longer bumps updatedAt or emits issue.updated.
This commit is contained in:
1 parent
f44590b8ca
commit
cb29c7d2c4
26 files changed
+1026
-16
No files matched your search
@@ -0,0 +1,42 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { StatsPeriod } from './stats-period.vo.js';
|
||||
import { InvalidStatsPeriodException } from '../exceptions/invalid-stats-period.exception.js';
|
||||
|
||||
describe('StatsPeriod', () => {
|
||||
const now = new Date('2026-10-07T22:30:00.000Z');
|
||||
|
||||
it('defaults to the last 30 days up to today in UTC', () => {
|
||||
const period = StatsPeriod.create({}, now);
|
||||
|
||||
expect(period.from).toBe('2026-09-08');
|
||||
expect(period.to).toBe('2026-10-07');
|
||||
expect(period.timeZone).toBe('UTC');
|
||||
expect(period.days).toHaveLength(30);
|
||||
});
|
||||
|
||||
it('takes "today" in the requested time zone', () => {
|
||||
// 22:30 UTC — в Москве уже 8 октября
|
||||
expect(StatsPeriod.create({ timeZone: 'Europe/Moscow' }, now).to).toBe('2026-10-08');
|
||||
});
|
||||
|
||||
it('lists every day of an explicit period inclusively', () => {
|
||||
const period = StatsPeriod.create({ from: '2026-02-27', to: '2026-03-02' }, now);
|
||||
|
||||
expect(period.days).toEqual(['2026-02-27', '2026-02-28', '2026-03-01', '2026-03-02']);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ from: '2026-10-05', to: '2026-10-01' }, 'from after to'],
|
||||
[{ from: '2026-02-30' }, 'non-existent date'],
|
||||
[{ to: '07.10.2026' }, 'wrong date format'],
|
||||
[{ from: '2025-01-01', to: '2026-10-07' }, 'period too long'],
|
||||
[{ timeZone: 'Mars/Olympus' }, 'unknown time zone'],
|
||||
[{ timeZone: '+03:00' }, 'offset instead of IANA name'],
|
||||
] as const)('rejects %j (%s)', (params, _reason) => {
|
||||
expect(() => StatsPeriod.create(params, now)).toThrow(InvalidStatsPeriodException);
|
||||
});
|
||||
|
||||
it('accepts the maximum period length', () => {
|
||||
expect(StatsPeriod.create({ from: '2025-10-07', to: '2026-10-07' }, now).days).toHaveLength(StatsPeriod.MAX_DAYS);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user