fix(server): test & schema

This commit is contained in:
DarkSky
2026-05-04 03:56:14 +08:00
parent 74d5ebad13
commit 1ad088398f
4 changed files with 119 additions and 72 deletions
@@ -209,47 +209,77 @@ test('listAccounts includes calendars count', async t => {
t.is(counts.get(accountB.id), 1);
});
test('assertCanLinkProvider blocks new google calendar accounts when disabled', async t => {
config.calendar.google.allowNewAccounts = false;
const user = await module.create(Mockers.User);
test.serial(
'assertCanLinkProvider blocks new google calendar accounts when disabled',
async t => {
config.calendar.google.allowNewAccounts = false;
const user = await module.create(Mockers.User);
const error = await t.throwsAsync(
calendarService.assertCanLinkProvider(user.id, CalendarProviderName.Google)
);
t.true(error instanceof GraphqlBadRequest);
t.is(
(error as GraphqlBadRequest).data?.code,
'calendar_provider_link_disabled'
);
});
const error = await t.throwsAsync(
calendarService.assertCanLinkProvider(
user.id,
CalendarProviderName.Google
)
);
t.true(error instanceof GraphqlBadRequest);
t.is(
(error as GraphqlBadRequest).data?.code,
'calendar_provider_link_disabled'
);
}
);
test('assertCanLinkProvider allows users with an existing google calendar account', async t => {
config.calendar.google.allowNewAccounts = false;
const user = await module.create(Mockers.User);
await createAccount(user.id);
test.serial(
'assertCanLinkProvider allows users with an existing google calendar account',
async t => {
config.calendar.google.allowNewAccounts = false;
const user = await module.create(Mockers.User);
await createAccount(user.id);
await t.notThrowsAsync(
calendarService.assertCanLinkProvider(user.id, CalendarProviderName.Google)
);
});
await t.notThrowsAsync(
calendarService.assertCanLinkProvider(
user.id,
CalendarProviderName.Google
)
);
}
);
test('handleOAuthCallback does not persist new google account when linking is disabled', async t => {
config.calendar.google.allowNewAccounts = false;
const provider = new MockCalendarProvider();
providerFactory.register(provider);
const user = await module.create(Mockers.User);
test.serial(
'handleOAuthCallback does not persist new google account when linking is disabled',
async t => {
config.calendar.google.allowNewAccounts = false;
const provider = new MockCalendarProvider();
mock.method(providerFactory, 'get', () => provider);
const user = await module.create(Mockers.User);
const error = await t.throwsAsync(
calendarService.handleOAuthCallback({
provider: CalendarProviderName.Google,
code: 'code',
redirectUri: 'https://example.com/callback',
userId: user.id,
})
);
t.true(error instanceof GraphqlBadRequest);
t.is((await models.calendarAccount.listByUser(user.id)).length, 0);
});
const error = await t.throwsAsync(
calendarService.handleOAuthCallback({
provider: CalendarProviderName.Google,
code: 'code',
redirectUri: 'https://example.com/callback',
userId: user.id,
})
);
t.true(error instanceof GraphqlBadRequest);
t.is((await models.calendarAccount.listByUser(user.id)).length, 0);
}
);
test.serial(
'canLinkProvider returns false for new google calendar accounts when disabled',
async t => {
config.calendar.google.allowNewAccounts = false;
const user = await module.create(Mockers.User);
t.false(
await calendarService.canLinkProvider(
user.id,
CalendarProviderName.Google
)
);
}
);
test('syncSubscription resets invalid sync token and maps events', async t => {
const user = await module.create(Mockers.User);
@@ -33,17 +33,20 @@ import {
export class CalendarServerConfigResolver {
constructor(
private readonly providerFactory: CalendarProviderFactory,
private readonly config: Config
private readonly config: Config,
private readonly calendar: CalendarService
) {}
@ResolveField(() => [CalendarProviderName])
calendarProviders() {
return this.providerFactory.providers.filter(provider => {
return (
provider !== CalendarProviderName.Google ||
this.config.calendar.google.allowNewAccounts !== false
);
});
async calendarProviders(@CurrentUser() user?: CurrentUser) {
const providers = [];
for (const provider of this.providerFactory.providers) {
if (!(await this.calendar.canLinkProvider(user?.id, provider))) {
continue;
}
providers.push(provider);
}
return providers;
}
@ResolveField(() => [CalendarCalDAVProviderPresetObjectType])
@@ -571,18 +571,28 @@ export class CalendarService {
}
async assertCanLinkProvider(userId: string, provider: CalendarProviderName) {
if (this.canCreateNewAccounts(provider)) {
return;
}
const accounts = await this.models.calendarAccount.listByUser(userId);
if (accounts.some(account => account.provider === provider)) {
if (await this.canLinkProvider(userId, provider)) {
return;
}
throw this.providerLinkDisabledError(provider);
}
async canLinkProvider(
userId: string | null | undefined,
provider: CalendarProviderName
) {
if (this.canCreateNewAccounts(provider)) {
return true;
}
if (!userId) {
return false;
}
const accounts = await this.models.calendarAccount.listByUser(userId);
return accounts.some(account => account.provider === provider);
}
getAuthUrl(
provider: CalendarProviderName,
state: string,