mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-01 15:47:41 +08:00
feat(core): Add Git connection database table (no-changelog) (#36268)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+16
@@ -63,6 +63,7 @@ Auto-generated from the PostgreSQL migrations in @n8n/db. Do not edit by hand.
|
||||
| [public.execution_metadata](public.execution_metadata.md) | 4 | | BASE TABLE |
|
||||
| [public.folder](public.folder.md) | 6 | | BASE TABLE |
|
||||
| [public.folder_tag](public.folder_tag.md) | 2 | | BASE TABLE |
|
||||
| [public.git_connection](public.git_connection.md) | 13 | | BASE TABLE |
|
||||
| [public.insights_by_period](public.insights_by_period.md) | 6 | | BASE TABLE |
|
||||
| [public.insights_metadata](public.insights_metadata.md) | 5 | | BASE TABLE |
|
||||
| [public.insights_raw](public.insights_raw.md) | 5 | | BASE TABLE |
|
||||
@@ -907,6 +908,21 @@ erDiagram
|
||||
varchar_36_ folderId FK
|
||||
varchar_36_ tagId FK
|
||||
}
|
||||
"public.git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
timestamp_3__with_time_zone createdAt
|
||||
text encryptedPassword
|
||||
text encryptedPrivateKey
|
||||
text encryptedUsername
|
||||
varchar_36_ id
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
text publicKey
|
||||
text repositoryUrl
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
"public.insights_by_period" {
|
||||
integer id
|
||||
integer metaId FK
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# public.git_connection
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| baseCommit | varchar(64) | | true | | | Last commit successfully reconciled for this connection; the base for three-way reconciliation. |
|
||||
| branchName | varchar(255) | | true | | | |
|
||||
| connectionType | varchar(16) | | false | | | GitConnectionType enum: "ssh", "https" |
|
||||
| createdAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
| encryptedPassword | text | | true | | | Encrypted HTTPS password/token; set when connectionType is 'https', null for 'ssh'. |
|
||||
| encryptedPrivateKey | text | | true | | | Encrypted SSH private key; set when connectionType is 'ssh', null for 'https'. |
|
||||
| encryptedUsername | text | | true | | | Encrypted HTTPS username; set when connectionType is 'https', null for 'ssh'. |
|
||||
| id | varchar(36) | | false | | | |
|
||||
| keyGeneratorType | varchar(16) | | true | | | GitKeyGeneratorType enum: "ed25519", "rsa" |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| publicKey | text | | true | | | SSH public key; set when connectionType is 'ssh', null for 'https'. |
|
||||
| repositoryUrl | text | | false | | | |
|
||||
| updatedAt | timestamp(3) with time zone | CURRENT_TIMESTAMP(3) | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| CHK_git_connection_connectionType | CHECK | CHECK ((("connectionType")::text = ANY ((ARRAY['ssh'::character varying, 'https'::character varying])::text[]))) |
|
||||
| CHK_git_connection_https_auth | CHECK | CHECK (((("connectionType")::text <> 'https'::text) OR (("publicKey" IS NULL) AND ("encryptedPrivateKey" IS NULL) AND ("keyGeneratorType" IS NULL)))) |
|
||||
| CHK_git_connection_keyGeneratorType | CHECK | CHECK ((("keyGeneratorType")::text = ANY ((ARRAY['ed25519'::character varying, 'rsa'::character varying])::text[]))) |
|
||||
| CHK_git_connection_ssh_auth | CHECK | CHECK (((("connectionType")::text <> 'ssh'::text) OR (("encryptedUsername" IS NULL) AND ("encryptedPassword" IS NULL)))) |
|
||||
| PK_92966b7ba4ce2cf61a62017a6ff | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| git_connection_connectionType_not_null | n | NOT NULL "connectionType" |
|
||||
| git_connection_createdAt_not_null | n | NOT NULL "createdAt" |
|
||||
| git_connection_id_not_null | n | NOT NULL id |
|
||||
| git_connection_name_not_null | n | NOT NULL name |
|
||||
| git_connection_repositoryUrl_not_null | n | NOT NULL "repositoryUrl" |
|
||||
| git_connection_updatedAt_not_null | n | NOT NULL "updatedAt" |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| PK_92966b7ba4ce2cf61a62017a6ff | CREATE UNIQUE INDEX "PK_92966b7ba4ce2cf61a62017a6ff" ON public.git_connection USING btree (id) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
|
||||
"public.git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
timestamp_3__with_time_zone createdAt
|
||||
text encryptedPassword
|
||||
text encryptedPrivateKey
|
||||
text encryptedUsername
|
||||
varchar_36_ id
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
text publicKey
|
||||
text repositoryUrl
|
||||
timestamp_3__with_time_zone updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
Generated
+16
@@ -63,6 +63,7 @@ Auto-generated from the SQLite migrations in @n8n/db. Do not edit by hand.
|
||||
| [execution_metadata](execution_metadata.md) | 4 | | table |
|
||||
| [folder](folder.md) | 6 | | table |
|
||||
| [folder_tag](folder_tag.md) | 2 | | table |
|
||||
| [git_connection](git_connection.md) | 13 | | table |
|
||||
| [insights_by_period](insights_by_period.md) | 6 | | table |
|
||||
| [insights_metadata](insights_metadata.md) | 5 | | table |
|
||||
| [insights_raw](insights_raw.md) | 5 | | table |
|
||||
@@ -894,6 +895,21 @@ erDiagram
|
||||
varchar_36_ folderId PK
|
||||
varchar_36_ tagId PK
|
||||
}
|
||||
"git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
datetime_3_ createdAt
|
||||
TEXT encryptedPassword
|
||||
TEXT encryptedPrivateKey
|
||||
TEXT encryptedUsername
|
||||
varchar_36_ id PK
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
TEXT publicKey
|
||||
TEXT repositoryUrl
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
"insights_by_period" {
|
||||
INTEGER id
|
||||
INTEGER metaId FK
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# git_connection
|
||||
|
||||
## Description
|
||||
|
||||
<details>
|
||||
<summary><strong>Table Definition</strong></summary>
|
||||
|
||||
```sql
|
||||
CREATE TABLE "git_connection" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128) NOT NULL, "repositoryUrl" text NOT NULL, "branchName" varchar(255), "connectionType" varchar(16) NOT NULL, "publicKey" text, "encryptedPrivateKey" text, "encryptedUsername" text, "encryptedPassword" text, "keyGeneratorType" varchar(16), "baseCommit" varchar(64), "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), CONSTRAINT "CHK_git_connection_ssh_auth" CHECK ("connectionType" <> 'ssh' OR ("encryptedUsername" IS NULL AND "encryptedPassword" IS NULL)), CONSTRAINT "CHK_git_connection_https_auth" CHECK ("connectionType" <> 'https' OR ("publicKey" IS NULL AND "encryptedPrivateKey" IS NULL AND "keyGeneratorType" IS NULL)), CONSTRAINT "CHK_git_connection_connectionType" CHECK ("connectionType" IN ('ssh', 'https')), CONSTRAINT "CHK_git_connection_keyGeneratorType" CHECK ("keyGeneratorType" IN ('ed25519', 'rsa')))
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Columns
|
||||
|
||||
| Name | Type | Default | Nullable | Children | Parents | Comment |
|
||||
| ---- | ---- | ------- | -------- | -------- | ------- | ------- |
|
||||
| baseCommit | varchar(64) | | true | | | |
|
||||
| branchName | varchar(255) | | true | | | |
|
||||
| connectionType | varchar(16) | | false | | | |
|
||||
| createdAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
| encryptedPassword | TEXT | | true | | | |
|
||||
| encryptedPrivateKey | TEXT | | true | | | |
|
||||
| encryptedUsername | TEXT | | true | | | |
|
||||
| id | varchar(36) | | false | | | |
|
||||
| keyGeneratorType | varchar(16) | | true | | | |
|
||||
| name | varchar(128) | | false | | | |
|
||||
| publicKey | TEXT | | true | | | |
|
||||
| repositoryUrl | TEXT | | false | | | |
|
||||
| updatedAt | datetime(3) | STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') | false | | | |
|
||||
|
||||
## Constraints
|
||||
|
||||
| Name | Type | Definition |
|
||||
| ---- | ---- | ---------- |
|
||||
| - | CHECK | CHECK ("connectionType" <> 'ssh' OR ("encryptedUsername" IS NULL AND "encryptedPassword" IS NULL)) |
|
||||
| - | CHECK | CHECK ("connectionType" <> 'https' OR ("publicKey" IS NULL AND "encryptedPrivateKey" IS NULL AND "keyGeneratorType" IS NULL)) |
|
||||
| - | CHECK | CHECK ("connectionType" IN ('ssh', 'https')) |
|
||||
| - | CHECK | CHECK ("keyGeneratorType" IN ('ed25519', 'rsa')) |
|
||||
| id | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
| sqlite_autoindex_git_connection_1 | PRIMARY KEY | PRIMARY KEY (id) |
|
||||
|
||||
## Indexes
|
||||
|
||||
| Name | Definition |
|
||||
| ---- | ---------- |
|
||||
| sqlite_autoindex_git_connection_1 | PRIMARY KEY (id) |
|
||||
|
||||
## Relations
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
|
||||
|
||||
"git_connection" {
|
||||
varchar_64_ baseCommit
|
||||
varchar_255_ branchName
|
||||
varchar_16_ connectionType
|
||||
datetime_3_ createdAt
|
||||
TEXT encryptedPassword
|
||||
TEXT encryptedPrivateKey
|
||||
TEXT encryptedUsername
|
||||
varchar_36_ id PK
|
||||
varchar_16_ keyGeneratorType
|
||||
varchar_128_ name
|
||||
TEXT publicKey
|
||||
TEXT repositoryUrl
|
||||
datetime_3_ updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> Generated by [tbls](https://github.com/k1LoW/tbls)
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { MigrationContext, ReversibleMigration } from '../migration-types';
|
||||
|
||||
export class CreateGitConnectionTable1787056876306 implements ReversibleMigration {
|
||||
async up({ schemaBuilder: { createTable, column }, tablePrefix }: MigrationContext) {
|
||||
await createTable('git_connection')
|
||||
.withColumns(
|
||||
column('id').varchar(36).primary,
|
||||
column('name').varchar(128).notNull,
|
||||
column('repositoryUrl').text.notNull,
|
||||
column('branchName').varchar(255),
|
||||
column('connectionType')
|
||||
.varchar(16)
|
||||
.notNull.withEnumCheck(['ssh', 'https'])
|
||||
.comment('GitConnectionType enum: "ssh", "https"'),
|
||||
column('publicKey').text.comment(
|
||||
"SSH public key; set when connectionType is 'ssh', null for 'https'.",
|
||||
),
|
||||
column('encryptedPrivateKey').text.comment(
|
||||
"Encrypted SSH private key; set when connectionType is 'ssh', null for 'https'.",
|
||||
),
|
||||
column('encryptedUsername').text.comment(
|
||||
"Encrypted HTTPS username; set when connectionType is 'https', null for 'ssh'.",
|
||||
),
|
||||
column('encryptedPassword').text.comment(
|
||||
"Encrypted HTTPS password/token; set when connectionType is 'https', null for 'ssh'.",
|
||||
),
|
||||
column('keyGeneratorType')
|
||||
.varchar(16)
|
||||
.withEnumCheck(['ed25519', 'rsa'])
|
||||
.comment('GitKeyGeneratorType enum: "ed25519", "rsa"'),
|
||||
column('baseCommit')
|
||||
.varchar(64)
|
||||
.comment(
|
||||
'Last commit successfully reconciled for this connection; the base for three-way reconciliation.',
|
||||
),
|
||||
)
|
||||
// Credentials may be filled in after creation, but a connection must never carry
|
||||
// columns that belong to the other connectionType.
|
||||
.withCheck(
|
||||
`CHK_${tablePrefix}git_connection_ssh_auth`,
|
||||
'"connectionType" <> \'ssh\' OR ("encryptedUsername" IS NULL AND "encryptedPassword" IS NULL)',
|
||||
)
|
||||
.withCheck(
|
||||
`CHK_${tablePrefix}git_connection_https_auth`,
|
||||
'"connectionType" <> \'https\' OR ("publicKey" IS NULL AND "encryptedPrivateKey" IS NULL' +
|
||||
' AND "keyGeneratorType" IS NULL)',
|
||||
).withTimestamps;
|
||||
}
|
||||
|
||||
async down({ schemaBuilder: { dropTable } }: MigrationContext) {
|
||||
await dropTable('git_connection');
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { WithTimestampsAndStringId } from '@n8n/db';
|
||||
import { Column, Entity } from '@n8n/typeorm';
|
||||
|
||||
// Defined inline so this DB-layer entity stays self-contained; the shared
|
||||
// @n8n/api-types DTOs that own these unions arrive in a later PR.
|
||||
type GitConnectionType = 'ssh' | 'https';
|
||||
type GitKeyGeneratorType = 'ed25519' | 'rsa';
|
||||
|
||||
@Entity('git_connection')
|
||||
export class GitConnection extends WithTimestampsAndStringId {
|
||||
@Column({ type: 'varchar', length: 128 })
|
||||
name: string;
|
||||
|
||||
@Column({ type: 'text' })
|
||||
repositoryUrl: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
branchName: string | null;
|
||||
|
||||
@Column({ type: 'varchar', length: 16 })
|
||||
connectionType: GitConnectionType;
|
||||
|
||||
/** SSH public key; set when {@link connectionType} is `ssh`, null for `https`. */
|
||||
@Column({ type: 'text', nullable: true })
|
||||
publicKey: string | null;
|
||||
|
||||
/** Encrypted SSH private key; set when {@link connectionType} is `ssh`, null for `https`. */
|
||||
@Column({ type: 'text', nullable: true })
|
||||
encryptedPrivateKey: string | null;
|
||||
|
||||
/** Encrypted HTTPS username; set when {@link connectionType} is `https`, null for `ssh`. */
|
||||
@Column({ type: 'text', nullable: true })
|
||||
encryptedUsername: string | null;
|
||||
|
||||
/** Encrypted HTTPS password/token; set when {@link connectionType} is `https`, null for `ssh`. */
|
||||
@Column({ type: 'text', nullable: true })
|
||||
encryptedPassword: string | null;
|
||||
|
||||
/** Algorithm used to generate the SSH key pair; null for `https`. */
|
||||
@Column({ type: 'varchar', length: 16, nullable: true })
|
||||
keyGeneratorType: GitKeyGeneratorType | null;
|
||||
|
||||
/** Last commit successfully reconciled for this connection; the base for three-way reconciliation. */
|
||||
@Column({ type: 'varchar', length: 64, nullable: true })
|
||||
baseCommit: string | null;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
import {
|
||||
createTestMigrationContext,
|
||||
initDbUpToMigration,
|
||||
runSingleMigration,
|
||||
type TestMigrationContext,
|
||||
} from '@n8n/backend-test-utils';
|
||||
import { DbConnection } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { DataSource } from '@n8n/typeorm';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
const MIGRATION_NAME = 'CreateGitConnectionTable1787056876306';
|
||||
|
||||
describe('CreateGitConnectionTable migration', () => {
|
||||
let dataSource: DataSource;
|
||||
|
||||
beforeAll(async () => {
|
||||
const dbConnection = Container.get(DbConnection);
|
||||
await dbConnection.init();
|
||||
dataSource = Container.get(DataSource);
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await context.queryRunner.clearDatabase();
|
||||
await context.queryRunner.release();
|
||||
await initDbUpToMigration(MIGRATION_NAME);
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await Container.get(DbConnection).close();
|
||||
});
|
||||
|
||||
type ConnectionOverrides = {
|
||||
connectionType?: string;
|
||||
publicKey?: string | null;
|
||||
encryptedPrivateKey?: string | null;
|
||||
encryptedUsername?: string | null;
|
||||
encryptedPassword?: string | null;
|
||||
keyGeneratorType?: string | null;
|
||||
};
|
||||
|
||||
// Credential columns default to a valid set for the connectionType so rows pass
|
||||
// the ssh/https auth CHECK constraints unless a test deliberately overrides them.
|
||||
function credentialDefaults(connectionType: string) {
|
||||
if (connectionType === 'ssh') {
|
||||
return {
|
||||
publicKey: 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5',
|
||||
encryptedPrivateKey: 'encrypted-private-key',
|
||||
encryptedUsername: null,
|
||||
encryptedPassword: null,
|
||||
keyGeneratorType: 'ed25519',
|
||||
};
|
||||
}
|
||||
return {
|
||||
publicKey: null,
|
||||
encryptedPrivateKey: null,
|
||||
encryptedUsername: 'encrypted-username',
|
||||
encryptedPassword: 'encrypted-password',
|
||||
keyGeneratorType: null,
|
||||
};
|
||||
}
|
||||
|
||||
async function insertConnection(
|
||||
context: TestMigrationContext,
|
||||
overrides: ConnectionOverrides = {},
|
||||
) {
|
||||
const table = context.escape.tableName('git_connection');
|
||||
const now = new Date();
|
||||
const connectionType = overrides.connectionType ?? 'https';
|
||||
await context.runQuery(
|
||||
`INSERT INTO ${table} ("id", "name", "repositoryUrl", "connectionType", "publicKey", "encryptedPrivateKey", "encryptedUsername", "encryptedPassword", "keyGeneratorType", "createdAt", "updatedAt")
|
||||
VALUES (:id, :name, :repositoryUrl, :connectionType, :publicKey, :encryptedPrivateKey, :encryptedUsername, :encryptedPassword, :keyGeneratorType, :createdAt, :updatedAt)`,
|
||||
{
|
||||
id: randomUUID(),
|
||||
name: 'Deployments',
|
||||
repositoryUrl: 'https://example.com/org/repo.git',
|
||||
connectionType,
|
||||
...credentialDefaults(connectionType),
|
||||
...overrides,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
describe('Up migration', () => {
|
||||
it('creates a table that stores SSH and HTTPS connections', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const table = context.escape.tableName('git_connection');
|
||||
await insertConnection(context, { connectionType: 'https' });
|
||||
await insertConnection(context, { connectionType: 'ssh', keyGeneratorType: 'ed25519' });
|
||||
|
||||
const rows = await context.runQuery<
|
||||
Array<{ branchName: string | null; connectionType: string }>
|
||||
>(`SELECT "branchName", "connectionType" FROM ${table} ORDER BY "connectionType"`);
|
||||
expect(rows).toEqual([
|
||||
{ branchName: null, connectionType: 'https' },
|
||||
{ branchName: null, connectionType: 'ssh' },
|
||||
]);
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('rejects an out-of-set connectionType via the CHECK constraint', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(insertConnection(context, { connectionType: 'bogus' })).rejects.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('rejects an out-of-set keyGeneratorType via the CHECK constraint', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, { connectionType: 'ssh', keyGeneratorType: 'bogus' }),
|
||||
).rejects.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('accepts a NULL keyGeneratorType (unset for HTTPS connections)', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, { connectionType: 'https', keyGeneratorType: null }),
|
||||
).resolves.not.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('accepts an SSH connection whose key material is not set yet', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, {
|
||||
connectionType: 'ssh',
|
||||
publicKey: null,
|
||||
encryptedPrivateKey: null,
|
||||
keyGeneratorType: null,
|
||||
}),
|
||||
).resolves.not.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('accepts an HTTPS connection whose credentials are not set yet', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, {
|
||||
connectionType: 'https',
|
||||
encryptedUsername: null,
|
||||
encryptedPassword: null,
|
||||
}),
|
||||
).resolves.not.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('rejects an SSH connection that also carries HTTPS credentials via the auth CHECK constraint', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, { connectionType: 'ssh', encryptedUsername: 'leaked' }),
|
||||
).rejects.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
|
||||
it('rejects an HTTPS connection that also carries SSH key material via the auth CHECK constraint', async () => {
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
await expect(
|
||||
insertConnection(context, { connectionType: 'https', publicKey: 'ssh-ed25519 AAAA' }),
|
||||
).rejects.toThrow();
|
||||
await context.queryRunner.release();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Down migration', () => {
|
||||
it('drops the table and can be re-applied', async () => {
|
||||
await dataSource.undoLastMigration({ transaction: 'each' });
|
||||
|
||||
const context = createTestMigrationContext(dataSource);
|
||||
const table = `${context.tablePrefix}git_connection`;
|
||||
expect(await context.queryRunner.hasTable(table)).toBe(false);
|
||||
await context.queryRunner.release();
|
||||
|
||||
// Round-trip: up() must run cleanly again after a full revert.
|
||||
await runSingleMigration(MIGRATION_NAME);
|
||||
const context2 = createTestMigrationContext(dataSource);
|
||||
expect(await context2.queryRunner.hasTable(table)).toBe(true);
|
||||
await context2.queryRunner.release();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user