mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-04 19:15:33 +08:00
ffc27af3ba
fix #14780 #### PR Dependency Tree * **PR #14784** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **Bug Fixes** * Improved upgrade availability detection to properly compare semantic versions, including support for prerelease and canary versions. The system now accurately identifies when new versions are available, ensuring users receive timely update notifications. * **Tests** * Added comprehensive unit tests for version comparison and upgrade detection functionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
261 lines
6.1 KiB
TypeScript
261 lines
6.1 KiB
TypeScript
import { Controller, Get } from '@nestjs/common';
|
|
import test from 'ava';
|
|
import Sinon from 'sinon';
|
|
|
|
import { AppModule } from '../app.module';
|
|
import {
|
|
CANARY_CLIENT_VERSION_MAX_AGE_DAYS,
|
|
ConfigFactory,
|
|
hasNewerVersion,
|
|
UseNamedGuard,
|
|
} from '../base';
|
|
import { Public } from '../core/auth/guard';
|
|
import { VersionService } from '../core/version/service';
|
|
import { createTestingApp, TestingApp } from './utils';
|
|
|
|
@Public()
|
|
@Controller('/guarded')
|
|
class GuardedController {
|
|
@UseNamedGuard('version')
|
|
@Get('/test')
|
|
test() {
|
|
return 'test';
|
|
}
|
|
}
|
|
|
|
let app: TestingApp;
|
|
let config: ConfigFactory;
|
|
let version: VersionService;
|
|
|
|
function checkVersion(enabled = true) {
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
enabled,
|
|
requiredVersion: '>=0.25.0',
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
function makeCanaryDateVersion(date: Date, build = '015') {
|
|
return `${date.getUTCFullYear()}.${date.getUTCMonth() + 1}.${date.getUTCDate()}-canary.${build}`;
|
|
}
|
|
|
|
test.before(async () => {
|
|
app = await createTestingApp({
|
|
imports: [AppModule],
|
|
controllers: [GuardedController],
|
|
});
|
|
|
|
version = app.get(VersionService, { strict: false });
|
|
config = app.get(ConfigFactory, { strict: false });
|
|
});
|
|
|
|
test.beforeEach(async () => {
|
|
Sinon.reset();
|
|
|
|
checkVersion(true);
|
|
});
|
|
|
|
test.after.always(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
test('should passthrough if version check is not enabled', async t => {
|
|
checkVersion(false);
|
|
|
|
const spy = Sinon.spy(version, 'checkVersion');
|
|
|
|
let res = await app.GET('/guarded/test');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.20.0');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
|
|
|
t.is(res.status, 200);
|
|
t.true(spy.notCalled);
|
|
spy.restore();
|
|
});
|
|
|
|
test('should enforce hard required version when version range is invalid', async t => {
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
requiredVersion: 'invalid',
|
|
},
|
|
},
|
|
});
|
|
|
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.25.0');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
'Unsupported client with version [invalid], required version is [>=0.25.0].'
|
|
);
|
|
});
|
|
|
|
test('should pass if client version is allowed', async t => {
|
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.25.0');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.26.0');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
requiredVersion: '>=0.25.0',
|
|
},
|
|
},
|
|
});
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.25.0');
|
|
|
|
t.is(res.status, 200);
|
|
});
|
|
|
|
test('should fail if client version is not set or invalid', async t => {
|
|
let res = await app.GET('/guarded/test');
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
'Unsupported client with version [unset_or_invalid], required version is [>=0.25.0].'
|
|
);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', 'invalid');
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
'Unsupported client with version [invalid], required version is [>=0.25.0].'
|
|
);
|
|
});
|
|
|
|
test('should tell upgrade if client version is lower than allowed', async t => {
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
requiredVersion: '>=0.26.0 <=0.27.0',
|
|
},
|
|
},
|
|
});
|
|
|
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.25.0');
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
'Unsupported client with version [0.25.0], required version is [>=0.26.0 <=0.27.0].'
|
|
);
|
|
});
|
|
|
|
test('should tell downgrade if client version is higher than allowed', async t => {
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
requiredVersion: '>=0.25.0 <=0.26.0',
|
|
},
|
|
},
|
|
});
|
|
|
|
let res = await app.GET('/guarded/test').set('x-affine-version', '0.27.0');
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
'Unsupported client with version [0.27.0], required version is [>=0.25.0 <=0.26.0].'
|
|
);
|
|
});
|
|
|
|
test('should test prerelease version', async t => {
|
|
config.override({
|
|
client: {
|
|
versionControl: {
|
|
requiredVersion: '>=0.25.0',
|
|
},
|
|
},
|
|
});
|
|
|
|
let res = await app
|
|
.GET('/guarded/test')
|
|
.set('x-affine-version', '0.25.0-canary.1');
|
|
|
|
// 0.25.0-canary.1 is lower than 0.25.0 obviously
|
|
t.is(res.status, 403);
|
|
|
|
res = await app
|
|
.GET('/guarded/test')
|
|
.set('x-affine-version', '0.26.0-canary.1');
|
|
|
|
t.is(res.status, 200);
|
|
|
|
res = await app.GET('/guarded/test').set('x-affine-version', '0.26.0-beta.2');
|
|
|
|
t.is(res.status, 200);
|
|
});
|
|
|
|
test('should allow recent canary date version in canary namespace', async t => {
|
|
const prevNamespace = env.NAMESPACE;
|
|
// @ts-expect-error test
|
|
env.NAMESPACE = 'dev';
|
|
|
|
try {
|
|
const res = await app
|
|
.GET('/guarded/test')
|
|
.set('x-affine-version', makeCanaryDateVersion(new Date(), '015'));
|
|
|
|
t.is(res.status, 200);
|
|
} finally {
|
|
// @ts-expect-error test
|
|
env.NAMESPACE = prevNamespace;
|
|
}
|
|
});
|
|
|
|
test('should reject old canary date version in canary namespace', async t => {
|
|
const prevNamespace = env.NAMESPACE;
|
|
// @ts-expect-error test
|
|
env.NAMESPACE = 'dev';
|
|
|
|
try {
|
|
const old = new Date(
|
|
Date.now() -
|
|
(CANARY_CLIENT_VERSION_MAX_AGE_DAYS + 1) * 24 * 60 * 60 * 1000
|
|
);
|
|
const oldVersion = makeCanaryDateVersion(old, '015');
|
|
|
|
const res = await app
|
|
.GET('/guarded/test')
|
|
.set('x-affine-version', oldVersion);
|
|
|
|
t.is(res.status, 403);
|
|
t.is(
|
|
res.body.message,
|
|
`Unsupported client with version [${oldVersion}], required version is [canary (within 2 months)].`
|
|
);
|
|
} finally {
|
|
// @ts-expect-error test
|
|
env.NAMESPACE = prevNamespace;
|
|
}
|
|
});
|
|
|
|
test('should compare release versions for available upgrades', t => {
|
|
t.false(hasNewerVersion('0.26.5', '0.26.4'));
|
|
t.false(hasNewerVersion('0.26.5', '0.26.5'));
|
|
t.true(hasNewerVersion('0.26.5', '0.26.6'));
|
|
t.true(hasNewerVersion('0.26.5', '0.26.6-beta.1'));
|
|
t.false(hasNewerVersion('0.26.6-beta.2', '0.26.6-beta.1'));
|
|
});
|