Merge pull request #34267 from backstage/freben/describe-each-databases
tests: use describe.each for database test iteration
This commit is contained in:
@@ -27,12 +27,12 @@ const keyBase = {
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('DatabaseKeyStore', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should store a key, %p',
|
||||
async databaseId => {
|
||||
describe.each(databases.eachSupportedId())(
|
||||
'DatabaseKeyStore, %p',
|
||||
databaseId => {
|
||||
it('should store a key', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await AuthDatabase.runMigrations(knex);
|
||||
|
||||
@@ -53,12 +53,9 @@ describe('DatabaseKeyStore', () => {
|
||||
DateTime.fromJSDate(items[0].createdAt).diffNow('seconds').seconds,
|
||||
),
|
||||
).toBeLessThan(10);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'should remove stored keys, %p',
|
||||
async databaseId => {
|
||||
it('should remove stored keys', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await AuthDatabase.runMigrations(knex);
|
||||
|
||||
@@ -124,6 +121,6 @@ describe('DatabaseKeyStore', () => {
|
||||
await expect(store.listKeys()).resolves.toEqual({
|
||||
items: [],
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -24,89 +24,80 @@ import { mockServices, TestDatabases } from '@backstage/backend-test-utils';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('KeyStores', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
const defaultConfigOptions = {
|
||||
auth: {
|
||||
keyStore: {
|
||||
provider: 'memory',
|
||||
},
|
||||
const defaultConfigOptions = {
|
||||
auth: {
|
||||
keyStore: {
|
||||
provider: 'memory',
|
||||
},
|
||||
};
|
||||
const defaultConfig = new ConfigReader(defaultConfigOptions);
|
||||
},
|
||||
};
|
||||
const defaultConfig = new ConfigReader(defaultConfigOptions);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'reads auth section from config, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const configSpy = jest.spyOn(defaultConfig, 'getOptionalConfig');
|
||||
const keyStore = await KeyStores.fromConfig(defaultConfig, {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
describe.each(databases.eachSupportedId())('KeyStores, %p', databaseId => {
|
||||
it('reads auth section from config', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const configSpy = jest.spyOn(defaultConfig, 'getOptionalConfig');
|
||||
const keyStore = await KeyStores.fromConfig(defaultConfig, {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
|
||||
expect(keyStore).toBeInstanceOf(MemoryKeyStore);
|
||||
expect(configSpy).toHaveBeenCalledWith('auth.keyStore');
|
||||
expect(
|
||||
defaultConfig
|
||||
.getOptionalConfig('auth.keyStore')
|
||||
?.getOptionalString('provider'),
|
||||
).toBe(defaultConfigOptions.auth.keyStore.provider);
|
||||
},
|
||||
);
|
||||
expect(keyStore).toBeInstanceOf(MemoryKeyStore);
|
||||
expect(configSpy).toHaveBeenCalledWith('auth.keyStore');
|
||||
expect(
|
||||
defaultConfig
|
||||
.getOptionalConfig('auth.keyStore')
|
||||
?.getOptionalString('provider'),
|
||||
).toBe(defaultConfigOptions.auth.keyStore.provider);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can handle without auth config, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const keyStore = await KeyStores.fromConfig(new ConfigReader({}), {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
expect(keyStore).toBeInstanceOf(DatabaseKeyStore);
|
||||
},
|
||||
);
|
||||
it('can handle without auth config', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const keyStore = await KeyStores.fromConfig(new ConfigReader({}), {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
expect(keyStore).toBeInstanceOf(DatabaseKeyStore);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can handle additional provider config, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
jest.spyOn(FirestoreKeyStore, 'verifyConnection').mockResolvedValue();
|
||||
const createSpy = jest.spyOn(FirestoreKeyStore, 'create');
|
||||
it('can handle additional provider config', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
jest.spyOn(FirestoreKeyStore, 'verifyConnection').mockResolvedValue();
|
||||
const createSpy = jest.spyOn(FirestoreKeyStore, 'create');
|
||||
|
||||
const configOptions = {
|
||||
auth: {
|
||||
keyStore: {
|
||||
provider: 'firestore',
|
||||
firestore: {
|
||||
projectId: 'my-project',
|
||||
keyFilename: 'cred.json',
|
||||
path: 'my-path',
|
||||
timeout: 100,
|
||||
host: 'localhost',
|
||||
port: 8088,
|
||||
ssl: false,
|
||||
},
|
||||
const configOptions = {
|
||||
auth: {
|
||||
keyStore: {
|
||||
provider: 'firestore',
|
||||
firestore: {
|
||||
projectId: 'my-project',
|
||||
keyFilename: 'cred.json',
|
||||
path: 'my-path',
|
||||
timeout: 100,
|
||||
host: 'localhost',
|
||||
port: 8088,
|
||||
ssl: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
const config = new ConfigReader(configOptions);
|
||||
const keyStore = await KeyStores.fromConfig(config, {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
},
|
||||
};
|
||||
const config = new ConfigReader(configOptions);
|
||||
const keyStore = await KeyStores.fromConfig(config, {
|
||||
logger: mockServices.logger.mock(),
|
||||
database: AuthDatabase.create(mockServices.database({ knex })),
|
||||
});
|
||||
|
||||
expect(keyStore).toBeInstanceOf(FirestoreKeyStore);
|
||||
expect(createSpy).toHaveBeenCalledWith(
|
||||
configOptions.auth.keyStore.firestore,
|
||||
);
|
||||
expect(
|
||||
config
|
||||
.getOptionalConfig('auth.keyStore')
|
||||
?.getOptionalConfig('firestore')
|
||||
?.getOptionalString('projectId'),
|
||||
).toBe(configOptions.auth.keyStore.firestore.projectId);
|
||||
},
|
||||
);
|
||||
expect(keyStore).toBeInstanceOf(FirestoreKeyStore);
|
||||
expect(createSpy).toHaveBeenCalledWith(
|
||||
configOptions.auth.keyStore.firestore,
|
||||
);
|
||||
expect(
|
||||
config
|
||||
.getOptionalConfig('auth.keyStore')
|
||||
?.getOptionalConfig('firestore')
|
||||
?.getOptionalString('projectId'),
|
||||
).toBe(configOptions.auth.keyStore.firestore.projectId);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -41,179 +41,154 @@ async function migrateUntilBefore(knex: Knex, target: string): Promise<void> {
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('migrations', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20230428155633_sessions.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
describe.each(databases.eachSupportedId())('migrations, %p', databaseId => {
|
||||
it('20230428155633_sessions.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
await migrateUntilBefore(knex, '20230428155633_sessions.js');
|
||||
await migrateUntilBefore(knex, '20230428155633_sessions.js');
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
// Ensure that large cookies are supported
|
||||
const data = `{"cookie":"${'a'.repeat(100_000)}"}`;
|
||||
await knex
|
||||
.insert({ sid: 'abc', expired: knex.fn.now(), sess: data })
|
||||
.into('sessions');
|
||||
await knex
|
||||
.insert({ sid: 'def', expired: knex.fn.now(), sess: data })
|
||||
.into('sessions');
|
||||
|
||||
await expect(knex('sessions').orderBy('sid', 'asc')).resolves.toEqual([
|
||||
{ sid: 'abc', expired: expect.anything(), sess: data },
|
||||
{ sid: 'def', expired: expect.anything(), sess: data },
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
it('20240510120825_user_info.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
await migrateUntilBefore(knex, '20240510120825_user_info.js');
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
const user_info = JSON.stringify({
|
||||
claims: {
|
||||
ent: ['group:default/group1', 'group:default/group2'],
|
||||
},
|
||||
});
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
|
||||
await expect(knex('user_info')).resolves.toEqual([
|
||||
{
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: expect.anything(),
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
it('20250707164600_user_created_at.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateUntilBefore(knex, '20250707164600_user_created_at.js');
|
||||
|
||||
if (knex.client.config.client.includes('sqlite')) {
|
||||
// Sqlite doesn't support adding a column with non-constant default when table has data
|
||||
// so we just test that the migration runs without errors
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
// Ensure that large cookies are supported
|
||||
const data = `{"cookie":"${'a'.repeat(100_000)}"}`;
|
||||
await knex
|
||||
.insert({ sid: 'abc', expired: knex.fn.now(), sess: data })
|
||||
.into('sessions');
|
||||
await knex
|
||||
.insert({ sid: 'def', expired: knex.fn.now(), sess: data })
|
||||
.into('sessions');
|
||||
return;
|
||||
}
|
||||
|
||||
await expect(knex('sessions').orderBy('sid', 'asc')).resolves.toEqual([
|
||||
{ sid: 'abc', expired: expect.anything(), sess: data },
|
||||
{ sid: 'def', expired: expect.anything(), sess: data },
|
||||
]);
|
||||
const user_info = JSON.stringify({
|
||||
claims: {
|
||||
ent: ['group:default/group1', 'group:default/group2'],
|
||||
},
|
||||
});
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
const { exp } = await knex('user_info').first();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20240510120825_user_info.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await migrateUntilBefore(knex, '20240510120825_user_info.js');
|
||||
await migrateUpOnce(knex);
|
||||
const { created_at, updated_at } = await knex('user_info').first();
|
||||
|
||||
const user_info = JSON.stringify({
|
||||
claims: {
|
||||
ent: ['group:default/group1', 'group:default/group2'],
|
||||
},
|
||||
});
|
||||
expect(updated_at).toEqual(exp);
|
||||
expect(created_at).toBeDefined();
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
updated_at: knex.fn.now(),
|
||||
})
|
||||
.into('user_info')
|
||||
.onConflict(['user_entity_ref'])
|
||||
.merge();
|
||||
|
||||
await expect(knex('user_info')).resolves.toEqual([
|
||||
{
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: expect.anything(),
|
||||
},
|
||||
]);
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user-2',
|
||||
user_info,
|
||||
updated_at: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
await expect(
|
||||
knex('user_info').select('created_at', 'updated_at'),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
},
|
||||
{
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
},
|
||||
]);
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20250707164600_user_created_at.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateUntilBefore(knex, '20250707164600_user_created_at.js');
|
||||
await expect(knex('user_info').select('exp')).resolves.toEqual([
|
||||
{ exp: expect.any(Date) },
|
||||
{ exp: expect.any(Date) },
|
||||
]);
|
||||
|
||||
if (knex.client.config.client.includes('sqlite')) {
|
||||
// Sqlite doesn't support adding a column with non-constant default when table has data
|
||||
// so we just test that the migration runs without errors
|
||||
await migrateUpOnce(knex);
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
it('20250909120000_oidc_client_registration.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
const user_info = JSON.stringify({
|
||||
claims: {
|
||||
ent: ['group:default/group1', 'group:default/group2'],
|
||||
},
|
||||
});
|
||||
await migrateUntilBefore(
|
||||
knex,
|
||||
'20250909120000_oidc_client_registration.js',
|
||||
);
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
exp: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
|
||||
const { exp } = await knex('user_info').first();
|
||||
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
const { created_at, updated_at } = await knex('user_info').first();
|
||||
|
||||
expect(updated_at).toEqual(exp);
|
||||
expect(created_at).toBeDefined();
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user',
|
||||
user_info,
|
||||
updated_at: knex.fn.now(),
|
||||
})
|
||||
.into('user_info')
|
||||
.onConflict(['user_entity_ref'])
|
||||
.merge();
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
user_entity_ref: 'user:default/backstage-user-2',
|
||||
user_info,
|
||||
updated_at: knex.fn.now(),
|
||||
})
|
||||
.into('user_info');
|
||||
|
||||
await expect(
|
||||
knex('user_info').select('created_at', 'updated_at'),
|
||||
).resolves.toEqual([
|
||||
{
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
},
|
||||
{
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await expect(knex('user_info').select('exp')).resolves.toEqual([
|
||||
{ exp: expect.any(Date) },
|
||||
{ exp: expect.any(Date) },
|
||||
]);
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20250909120000_oidc_client_registration.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
await migrateUntilBefore(
|
||||
knex,
|
||||
'20250909120000_oidc_client_registration.js',
|
||||
);
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'test-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'Test Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
scope: 'openid profile',
|
||||
metadata: JSON.stringify({ description: 'Test client' }),
|
||||
})
|
||||
.into('oidc_clients');
|
||||
|
||||
await expect(
|
||||
knex('oidc_clients').where('client_id', 'test-client-id').first(),
|
||||
).resolves.toEqual({
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'test-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'Test Client',
|
||||
@@ -222,230 +197,235 @@ describe('migrations', () => {
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
scope: 'openid profile',
|
||||
metadata: JSON.stringify({ description: 'Test client' }),
|
||||
});
|
||||
})
|
||||
.into('oidc_clients');
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-session-id',
|
||||
client_id: 'test-client-id',
|
||||
user_entity_ref: 'user:default/test-user',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
response_type: 'code',
|
||||
code_challenge: 'test-challenge',
|
||||
code_challenge_method: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
await expect(
|
||||
knex('oidc_clients').where('client_id', 'test-client-id').first(),
|
||||
).resolves.toEqual({
|
||||
client_id: 'test-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'Test Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
scope: 'openid profile',
|
||||
metadata: JSON.stringify({ description: 'Test client' }),
|
||||
});
|
||||
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-session-id')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-session-id',
|
||||
client_id: 'test-client-id',
|
||||
user_entity_ref: 'user:default/test-user',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
response_type: 'code',
|
||||
code_challenge: 'test-challenge',
|
||||
code_challenge_method: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
status: 'pending',
|
||||
}),
|
||||
);
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-session-id',
|
||||
client_id: 'test-client-id',
|
||||
user_entity_ref: 'user:default/test-user',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
response_type: 'code',
|
||||
code_challenge: 'test-challenge',
|
||||
code_challenge_method: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
code: 'test-auth-code',
|
||||
session_id: 'test-session-id',
|
||||
expires_at: new Date(Date.now() + 600000),
|
||||
used: false,
|
||||
})
|
||||
.into('oidc_authorization_codes');
|
||||
|
||||
await expect(
|
||||
knex('oidc_authorization_codes')
|
||||
.where('code', 'test-auth-code')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
code: 'test-auth-code',
|
||||
session_id: 'test-session-id',
|
||||
}),
|
||||
);
|
||||
|
||||
await knex('oauth_authorization_sessions')
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-session-id')
|
||||
.del();
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-session-id',
|
||||
client_id: 'test-client-id',
|
||||
user_entity_ref: 'user:default/test-user',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
scope: 'openid',
|
||||
state: 'test-state',
|
||||
response_type: 'code',
|
||||
code_challenge: 'test-challenge',
|
||||
code_challenge_method: 'S256',
|
||||
nonce: 'test-nonce',
|
||||
status: 'pending',
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(
|
||||
knex('oidc_authorization_codes').where('session_id', 'test-session-id'),
|
||||
).resolves.toHaveLength(0);
|
||||
await knex
|
||||
.insert({
|
||||
code: 'test-auth-code',
|
||||
session_id: 'test-session-id',
|
||||
expires_at: new Date(Date.now() + 600000),
|
||||
used: false,
|
||||
})
|
||||
.into('oidc_authorization_codes');
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
await expect(
|
||||
knex('oidc_authorization_codes').where('code', 'test-auth-code').first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
code: 'test-auth-code',
|
||||
session_id: 'test-session-id',
|
||||
}),
|
||||
);
|
||||
|
||||
const tables = [
|
||||
'oidc_clients',
|
||||
'oauth_authorization_sessions',
|
||||
'oidc_authorization_codes',
|
||||
];
|
||||
await knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-session-id')
|
||||
.del();
|
||||
|
||||
for (const table of tables) {
|
||||
await expect(knex.schema.hasTable(table)).resolves.toBe(false);
|
||||
}
|
||||
await expect(
|
||||
knex('oidc_authorization_codes').where('session_id', 'test-session-id'),
|
||||
).resolves.toHaveLength(0);
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20251118120000_oauth_state_text.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const tables = [
|
||||
'oidc_clients',
|
||||
'oauth_authorization_sessions',
|
||||
'oidc_authorization_codes',
|
||||
];
|
||||
|
||||
await migrateUntilBefore(knex, '20251118120000_oauth_state_text.js');
|
||||
for (const table of tables) {
|
||||
await expect(knex.schema.hasTable(table)).resolves.toBe(false);
|
||||
}
|
||||
|
||||
// First create a client for the foreign key constraint
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'test-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'Test Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
})
|
||||
.into('oidc_clients');
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
// Insert a session with state before migration
|
||||
const existingState = 'existing-short-state';
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-existing-session',
|
||||
client_id: 'test-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
state: existingState,
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
it('20251118120000_oauth_state_text.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
// Apply the migration that changes state to TEXT
|
||||
await migrateUpOnce(knex);
|
||||
await migrateUntilBefore(knex, '20251118120000_oauth_state_text.js');
|
||||
|
||||
// Verify existing state persists after migration
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-existing-session')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-existing-session',
|
||||
state: existingState,
|
||||
}),
|
||||
);
|
||||
// First create a client for the foreign key constraint
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'test-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'Test Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
})
|
||||
.into('oidc_clients');
|
||||
|
||||
// Test inserting a state parameter longer than 255 characters
|
||||
// This is based on the real-world example from the issue
|
||||
const longState = 'a'.repeat(280);
|
||||
// Insert a session with state before migration
|
||||
const existingState = 'existing-short-state';
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-existing-session',
|
||||
client_id: 'test-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
state: existingState,
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-long-state-session',
|
||||
client_id: 'test-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
state: longState,
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
// Apply the migration that changes state to TEXT
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-long-state-session')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-long-state-session',
|
||||
state: longState,
|
||||
}),
|
||||
);
|
||||
// Verify existing state persists after migration
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-existing-session')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-existing-session',
|
||||
state: existingState,
|
||||
}),
|
||||
);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
// Test inserting a state parameter longer than 255 characters
|
||||
// This is based on the real-world example from the issue
|
||||
const longState = 'a'.repeat(280);
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
await knex
|
||||
.insert({
|
||||
id: 'test-long-state-session',
|
||||
client_id: 'test-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
state: longState,
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20251217120000_drop_oidc_clients_fk.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions')
|
||||
.where('id', 'test-long-state-session')
|
||||
.first(),
|
||||
).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
id: 'test-long-state-session',
|
||||
state: longState,
|
||||
}),
|
||||
);
|
||||
|
||||
await migrateUntilBefore(knex, '20251217120000_drop_oidc_clients_fk.js');
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
// Create a client for DCR sessions
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'dcr-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'DCR Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
})
|
||||
.into('oidc_clients');
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
// Create a DCR session (has matching client in oidc_clients)
|
||||
await knex
|
||||
.insert({
|
||||
id: 'dcr-session',
|
||||
client_id: 'dcr-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
it('20251217120000_drop_oidc_clients_fk.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
// Apply migration - drops FK constraint
|
||||
await migrateUpOnce(knex);
|
||||
await migrateUntilBefore(knex, '20251217120000_drop_oidc_clients_fk.js');
|
||||
|
||||
// Now we can insert a CIMD session (URL-based client_id not in oidc_clients)
|
||||
await knex
|
||||
.insert({
|
||||
id: 'cimd-session',
|
||||
client_id: 'https://example.com/.well-known/oauth-client/cli',
|
||||
redirect_uri: 'http://localhost:8080/callback',
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
// Create a client for DCR sessions
|
||||
await knex
|
||||
.insert({
|
||||
client_id: 'dcr-client-id',
|
||||
client_secret: 'test-client-secret',
|
||||
client_name: 'DCR Client',
|
||||
response_types: JSON.stringify(['code']),
|
||||
grant_types: JSON.stringify(['authorization_code']),
|
||||
redirect_uris: JSON.stringify(['https://example.com/callback']),
|
||||
})
|
||||
.into('oidc_clients');
|
||||
|
||||
// Verify both sessions exist
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions').select('id').orderBy('id'),
|
||||
).resolves.toEqual([{ id: 'cimd-session' }, { id: 'dcr-session' }]);
|
||||
// Create a DCR session (has matching client in oidc_clients)
|
||||
await knex
|
||||
.insert({
|
||||
id: 'dcr-session',
|
||||
client_id: 'dcr-client-id',
|
||||
redirect_uri: 'https://example.com/callback',
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
|
||||
// Rollback - should delete CIMD sessions and re-add FK
|
||||
await migrateDownOnce(knex);
|
||||
// Apply migration - drops FK constraint
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
// CIMD session should be deleted, DCR session should remain
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions').select('id'),
|
||||
).resolves.toEqual([{ id: 'dcr-session' }]);
|
||||
// Now we can insert a CIMD session (URL-based client_id not in oidc_clients)
|
||||
await knex
|
||||
.insert({
|
||||
id: 'cimd-session',
|
||||
client_id: 'https://example.com/.well-known/oauth-client/cli',
|
||||
redirect_uri: 'http://localhost:8080/callback',
|
||||
response_type: 'code',
|
||||
status: 'pending',
|
||||
expires_at: new Date(Date.now() + 3600000),
|
||||
})
|
||||
.into('oauth_authorization_sessions');
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
// Verify both sessions exist
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions').select('id').orderBy('id'),
|
||||
).resolves.toEqual([{ id: 'cimd-session' }, { id: 'dcr-session' }]);
|
||||
|
||||
// Rollback - should delete CIMD sessions and re-add FK
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
// CIMD session should be deleted, DCR session should remain
|
||||
await expect(
|
||||
knex('oauth_authorization_sessions').select('id'),
|
||||
).resolves.toEqual([{ id: 'dcr-session' }]);
|
||||
|
||||
await knex.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user