Fix lint errors and add ast-grep rule for countAll
- Fix template literal expressions: wrap Date.now() in String() - Add missing afterAll import in admin.test.ts - Fix countOwners to use countAll() without misleading <number> type - Add ast-grep rule to prevent countAll<number>() usage - Fix formatting issues from merge conflict resolution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
8
.ast-grep/rules/no-countall-number.yml
Normal file
8
.ast-grep/rules/no-countall-number.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
id: no-countall-number
|
||||
language: typescript
|
||||
severity: error
|
||||
message: "Don't use countAll<number>() - use countAll() instead. PostgreSQL COUNT returns bigint (string), so the type annotation is misleading."
|
||||
note: "Use Number() to convert the result if you need a number type."
|
||||
rule:
|
||||
pattern: countAll<number>()
|
||||
fix: countAll()
|
||||
@@ -27,7 +27,7 @@
|
||||
import type { Database } from "@reviq/db-schema";
|
||||
import type { Kysely } from "kysely";
|
||||
import type { APIContext } from "../../context.js";
|
||||
import { beforeAll, describe, expect, test } from "bun:test";
|
||||
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
||||
import { call } from "@orpc/server";
|
||||
import {
|
||||
createTestUser,
|
||||
@@ -115,7 +115,9 @@ async function createOrg(
|
||||
logoUrl?: string;
|
||||
},
|
||||
): Promise<{ id: number; slug: string }> {
|
||||
const slug = options?.slug ?? `org-${String(Date.now())}-${String(Math.random()).slice(2, 8)}`;
|
||||
const slug =
|
||||
options?.slug ??
|
||||
`org-${String(Date.now())}-${String(Math.random()).slice(2, 8)}`;
|
||||
|
||||
const result = await db
|
||||
.insertInto("orgs")
|
||||
@@ -182,7 +184,8 @@ async function createLoginRequest(
|
||||
},
|
||||
): Promise<{ id: number; token: string }> {
|
||||
const token = `login-${String(Date.now())}${String(Math.random())}`;
|
||||
const expiresAt = options?.expiresAt ?? new Date(Date.now() + LOGIN_REQUEST_EXPIRY_MS);
|
||||
const expiresAt =
|
||||
options?.expiresAt ?? new Date(Date.now() + LOGIN_REQUEST_EXPIRY_MS);
|
||||
|
||||
const result = await db
|
||||
.insertInto("login_requests")
|
||||
@@ -225,7 +228,7 @@ async function createOrgInvite(
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return { id: Number(result.id) };
|
||||
return { id: result.id };
|
||||
}
|
||||
|
||||
describeE2E("admin", () => {
|
||||
@@ -263,11 +266,11 @@ describeE2E("admin", () => {
|
||||
).rejects.toThrow("No session or API key");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.users.list =====
|
||||
// ===== admin.users.list =====
|
||||
|
||||
describe("admin.users.list", () => {
|
||||
describe("admin.users.list", () => {
|
||||
test("returns all users", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -280,7 +283,9 @@ describe("admin.users.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const users = await call(router.admin.users.list, undefined, { context });
|
||||
const users = await call(router.admin.users.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(users.length).toBe(3);
|
||||
const emails = users.map((u) => u.email).sort();
|
||||
@@ -303,7 +308,9 @@ describe("admin.users.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const users = await call(router.admin.users.list, undefined, { context });
|
||||
const users = await call(router.admin.users.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
const adminUser = users.find((u) => u.email === "admin@example.com");
|
||||
expect(adminUser).toBeDefined();
|
||||
@@ -326,18 +333,20 @@ describe("admin.users.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const users = await call(router.admin.users.list, undefined, { context });
|
||||
const users = await call(router.admin.users.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
// Only the admin user exists
|
||||
expect(users.length).toBe(1);
|
||||
expect(users[0]?.email).toBe("onlyadmin@example.com");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.users.get =====
|
||||
// ===== admin.users.get =====
|
||||
|
||||
describe("admin.users.get", () => {
|
||||
describe("admin.users.get", () => {
|
||||
test("returns user by email", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -439,12 +448,12 @@ describe("admin.users.get", () => {
|
||||
expect(user.hasPassword).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.users.create =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
// ===== admin.users.create =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
|
||||
describe("admin.users.create", () => {
|
||||
describe("admin.users.create", () => {
|
||||
afterAll(async () => {
|
||||
// Clean up all test data
|
||||
await truncateAllTables(getSharedDb());
|
||||
@@ -452,7 +461,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("creates passwordless user", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -483,7 +492,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("creates user with name", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -510,7 +519,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("creates user and adds to organization as member", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -523,7 +532,10 @@ describe("admin.users.create", () => {
|
||||
|
||||
await call(
|
||||
router.admin.users.create,
|
||||
{ email: `orguser-${uniqueId}@example.com`, orgSlug: `test-org-${uniqueId}` },
|
||||
{
|
||||
email: `orguser-${uniqueId}@example.com`,
|
||||
orgSlug: `test-org-${uniqueId}`,
|
||||
},
|
||||
{ context },
|
||||
);
|
||||
|
||||
@@ -542,7 +554,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("creates user and adds to organization with custom role", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -555,7 +567,11 @@ describe("admin.users.create", () => {
|
||||
|
||||
await call(
|
||||
router.admin.users.create,
|
||||
{ email: `adminuser-${uniqueId}@example.com`, orgSlug: `test-org-${uniqueId}`, orgRole: "admin" },
|
||||
{
|
||||
email: `adminuser-${uniqueId}@example.com`,
|
||||
orgSlug: `test-org-${uniqueId}`,
|
||||
orgRole: "admin",
|
||||
},
|
||||
{ context },
|
||||
);
|
||||
|
||||
@@ -572,7 +588,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("normalizes email to lowercase", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -599,7 +615,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("throws CONFLICT for duplicate email", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -621,7 +637,7 @@ describe("admin.users.create", () => {
|
||||
|
||||
test("throws NOT_FOUND for non-existent org", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -634,16 +650,19 @@ describe("admin.users.create", () => {
|
||||
await expect(
|
||||
call(
|
||||
router.admin.users.create,
|
||||
{ email: `newuser-${uniqueId}@example.com`, orgSlug: "nonexistent-org" },
|
||||
{
|
||||
email: `newuser-${uniqueId}@example.com`,
|
||||
orgSlug: "nonexistent-org",
|
||||
},
|
||||
{ context },
|
||||
),
|
||||
).rejects.toThrow("Organization not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.users.update =====
|
||||
// ===== admin.users.update =====
|
||||
|
||||
describe("admin.users.update", () => {
|
||||
describe("admin.users.update", () => {
|
||||
test("grants superuser status", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -811,11 +830,11 @@ describe("admin.users.update", () => {
|
||||
).rejects.toThrow("User not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.users.confirmEmail =====
|
||||
// ===== admin.users.confirmEmail =====
|
||||
|
||||
describe("admin.users.confirmEmail", () => {
|
||||
describe("admin.users.confirmEmail", () => {
|
||||
test("confirms user email", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -917,11 +936,11 @@ describe("admin.users.confirmEmail", () => {
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.list =====
|
||||
// ===== admin.orgs.list =====
|
||||
|
||||
describe("admin.orgs.list", () => {
|
||||
describe("admin.orgs.list", () => {
|
||||
test("returns all organizations", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -982,11 +1001,11 @@ describe("admin.orgs.list", () => {
|
||||
expect(orgs).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.get =====
|
||||
// ===== admin.orgs.get =====
|
||||
|
||||
describe("admin.orgs.get", () => {
|
||||
describe("admin.orgs.get", () => {
|
||||
test("returns organization by slug", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -1029,25 +1048,27 @@ describe("admin.orgs.get", () => {
|
||||
).rejects.toThrow("Organization not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.create =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
// ===== admin.orgs.create =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
|
||||
describe("admin.orgs.create", () => {
|
||||
describe("admin.orgs.create", () => {
|
||||
afterAll(async () => {
|
||||
await truncateAllTables(getSharedDb());
|
||||
});
|
||||
|
||||
test("creates organization with owner", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
isSuperuser: true,
|
||||
});
|
||||
const owner = await createTestUser(db, { email: `owner-${uniqueId}@example.com` });
|
||||
const owner = await createTestUser(db, {
|
||||
email: `owner-${uniqueId}@example.com`,
|
||||
});
|
||||
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
@@ -1077,7 +1098,7 @@ describe("admin.orgs.create", () => {
|
||||
// Verify owner membership
|
||||
const membership = await db
|
||||
.selectFrom("org_members")
|
||||
.where("org_id", "=", org!.id)
|
||||
.where("org_id", "=", org?.id)
|
||||
.where("user_id", "=", owner.id)
|
||||
.selectAll()
|
||||
.executeTakeFirst();
|
||||
@@ -1088,7 +1109,7 @@ describe("admin.orgs.create", () => {
|
||||
|
||||
test("normalizes owner email to lowercase", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1114,7 +1135,7 @@ describe("admin.orgs.create", () => {
|
||||
|
||||
test("throws NOT_FOUND for non-existent owner", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1139,13 +1160,15 @@ describe("admin.orgs.create", () => {
|
||||
|
||||
test("throws CONFLICT for duplicate slug", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
isSuperuser: true,
|
||||
});
|
||||
const owner = await createTestUser(db, { email: `owner-${uniqueId}@example.com` });
|
||||
const owner = await createTestUser(db, {
|
||||
email: `owner-${uniqueId}@example.com`,
|
||||
});
|
||||
await createOrg(db, { slug: `existing-org-${uniqueId}` });
|
||||
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
@@ -1163,11 +1186,11 @@ describe("admin.orgs.create", () => {
|
||||
),
|
||||
).rejects.toThrow("Organization with this slug already exists");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.update =====
|
||||
// ===== admin.orgs.update =====
|
||||
|
||||
describe("admin.orgs.update", () => {
|
||||
describe("admin.orgs.update", () => {
|
||||
test("updates display name", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -1344,31 +1367,38 @@ describe("admin.orgs.update", () => {
|
||||
).rejects.toThrow("Organization not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.delete =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
// ===== admin.orgs.delete =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
|
||||
describe("admin.orgs.delete", () => {
|
||||
describe("admin.orgs.delete", () => {
|
||||
afterAll(async () => {
|
||||
await truncateAllTables(getSharedDb());
|
||||
});
|
||||
|
||||
test("deletes organization and related records", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
isSuperuser: true,
|
||||
});
|
||||
const member = await createTestUser(db, { email: `member-${uniqueId}@example.com` });
|
||||
const member = await createTestUser(db, {
|
||||
email: `member-${uniqueId}@example.com`,
|
||||
});
|
||||
const org = await createOrg(db, { slug: `delete-me-${uniqueId}` });
|
||||
|
||||
// Create related records
|
||||
await addOrgMember(db, org.id, member.id, "owner");
|
||||
await createSite(db, org.id, `example-${uniqueId}.com`);
|
||||
await createOrgInvite(db, org.id, `invite-${uniqueId}@example.com`, admin.id);
|
||||
await createOrgInvite(
|
||||
db,
|
||||
org.id,
|
||||
`invite-${uniqueId}@example.com`,
|
||||
admin.id,
|
||||
);
|
||||
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
@@ -1414,7 +1444,7 @@ describe("admin.orgs.delete", () => {
|
||||
|
||||
test("throws NOT_FOUND for non-existent organization", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1428,11 +1458,11 @@ describe("admin.orgs.delete", () => {
|
||||
call(router.admin.orgs.delete, { slug: "nonexistent" }, { context }),
|
||||
).rejects.toThrow("Organization not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.listSites =====
|
||||
// ===== admin.orgs.listSites =====
|
||||
|
||||
describe("admin.orgs.listSites", () => {
|
||||
describe("admin.orgs.listSites", () => {
|
||||
test("returns sites for organization", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -1514,23 +1544,27 @@ describe("admin.orgs.listSites", () => {
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
await expect(
|
||||
call(router.admin.orgs.listSites, { slug: "nonexistent" }, { context }),
|
||||
call(
|
||||
router.admin.orgs.listSites,
|
||||
{ slug: "nonexistent" },
|
||||
{ context },
|
||||
),
|
||||
).rejects.toThrow("Organization not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.addSite =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
// ===== admin.orgs.addSite =====
|
||||
// NOTE: These tests don't use withTestTransaction because the procedure uses db.transaction() internally
|
||||
|
||||
describe("admin.orgs.addSite", () => {
|
||||
describe("admin.orgs.addSite", () => {
|
||||
afterAll(async () => {
|
||||
await truncateAllTables(getSharedDb());
|
||||
});
|
||||
|
||||
test("adds site to organization", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1562,7 +1596,7 @@ describe("admin.orgs.addSite", () => {
|
||||
|
||||
test("throws NOT_FOUND for non-existent organization", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1583,7 +1617,7 @@ describe("admin.orgs.addSite", () => {
|
||||
|
||||
test("throws CONFLICT for duplicate domain", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1606,7 +1640,7 @@ describe("admin.orgs.addSite", () => {
|
||||
|
||||
test("throws CONFLICT for domain in another organization", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const admin = await createTestUser(db, {
|
||||
email: `admin-${uniqueId}@example.com`,
|
||||
@@ -1627,11 +1661,11 @@ describe("admin.orgs.addSite", () => {
|
||||
),
|
||||
).rejects.toThrow("Site with this domain already exists");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.orgs.removeSite =====
|
||||
// ===== admin.orgs.removeSite =====
|
||||
|
||||
describe("admin.orgs.removeSite", () => {
|
||||
describe("admin.orgs.removeSite", () => {
|
||||
test("removes site from organization", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -1727,11 +1761,11 @@ describe("admin.orgs.removeSite", () => {
|
||||
).rejects.toThrow("Site not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ===== admin.auth.completeLogin =====
|
||||
// ===== admin.auth.completeLogin =====
|
||||
|
||||
describe("admin.auth.completeLogin", () => {
|
||||
describe("admin.auth.completeLogin", () => {
|
||||
test("completes pending login request", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const admin = await createTestUser(db, {
|
||||
@@ -1739,7 +1773,11 @@ describe("admin.auth.completeLogin", () => {
|
||||
isSuperuser: true,
|
||||
});
|
||||
const user = await createTestUser(db, { email: "user@example.com" });
|
||||
const loginRequest = await createLoginRequest(db, user.id, "user@example.com");
|
||||
const loginRequest = await createLoginRequest(
|
||||
db,
|
||||
user.id,
|
||||
"user@example.com",
|
||||
);
|
||||
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
@@ -1770,7 +1808,11 @@ describe("admin.auth.completeLogin", () => {
|
||||
isSuperuser: true,
|
||||
});
|
||||
const user = await createTestUser(db, { email: "user@example.com" });
|
||||
const loginRequest = await createLoginRequest(db, user.id, "user@example.com");
|
||||
const loginRequest = await createLoginRequest(
|
||||
db,
|
||||
user.id,
|
||||
"user@example.com",
|
||||
);
|
||||
|
||||
const { token: sessionToken } = await createSession(db, admin.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
@@ -1890,12 +1932,16 @@ describe("admin.auth.completeLogin", () => {
|
||||
|
||||
expect(allRequests.length).toBe(2);
|
||||
|
||||
const completedCount = allRequests.filter((r) => r.completed_at !== null).length;
|
||||
const completedCount = allRequests.filter(
|
||||
(r) => r.completed_at !== null,
|
||||
).length;
|
||||
expect(completedCount).toBe(1);
|
||||
|
||||
const uncompletedCount = allRequests.filter((r) => r.completed_at === null).length;
|
||||
const uncompletedCount = allRequests.filter(
|
||||
(r) => r.completed_at === null,
|
||||
).length;
|
||||
expect(uncompletedCount).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}); // Close describeE2E("admin")
|
||||
|
||||
@@ -224,7 +224,7 @@ describeE2E("me", () => {
|
||||
const user = await createTestUser(db, { email: "expired@example.com" });
|
||||
|
||||
// Create an expired session
|
||||
const token = `expired-session-${Date.now()}`;
|
||||
const token = `expired-session-${String(Date.now())}`;
|
||||
const tokenHashValue = await hashToken(token);
|
||||
await db
|
||||
.insertInto("sessions")
|
||||
@@ -249,7 +249,7 @@ describeE2E("me", () => {
|
||||
const user = await createTestUser(db, { email: "revoked@example.com" });
|
||||
|
||||
// Create a revoked session
|
||||
const token = `revoked-session-${Date.now()}`;
|
||||
const token = `revoked-session-${String(Date.now())}`;
|
||||
const tokenHashValue = await hashToken(token);
|
||||
await db
|
||||
.insertInto("sessions")
|
||||
@@ -1583,7 +1583,7 @@ async function createOrgInvite(
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
return { id: Number(result.id) };
|
||||
return { id: result.id };
|
||||
}
|
||||
|
||||
describeE2E("me.apiTokens and me.invites", () => {
|
||||
@@ -1598,7 +1598,9 @@ describeE2E("me.apiTokens and me.invites", () => {
|
||||
const { token: sessionToken } = await createSession(db, user.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, { context });
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(tokens).toHaveLength(0);
|
||||
});
|
||||
@@ -1619,15 +1621,27 @@ describeE2E("me.apiTokens and me.invites", () => {
|
||||
await db
|
||||
.insertInto("api_tokens")
|
||||
.values([
|
||||
{ user_id: user.id, token_hash: tokenHash1, name: "Token One", expires_at: expiresAt },
|
||||
{ user_id: user.id, token_hash: tokenHash2, name: "Token Two", expires_at: expiresAt },
|
||||
{
|
||||
user_id: user.id,
|
||||
token_hash: tokenHash1,
|
||||
name: "Token One",
|
||||
expires_at: expiresAt,
|
||||
},
|
||||
{
|
||||
user_id: user.id,
|
||||
token_hash: tokenHash2,
|
||||
name: "Token Two",
|
||||
expires_at: expiresAt,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
|
||||
const { token: sessionToken } = await createSession(db, user.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, { context });
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(tokens).toHaveLength(2);
|
||||
const names = tokens.map((t) => t.name).sort();
|
||||
@@ -1640,8 +1654,14 @@ describeE2E("me.apiTokens and me.invites", () => {
|
||||
|
||||
test("only returns current user tokens", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const user1 = await createTestUser(db, { email: "user1@example.com", isSuperuser: true });
|
||||
const user2 = await createTestUser(db, { email: "user2@example.com", isSuperuser: true });
|
||||
const user1 = await createTestUser(db, {
|
||||
email: "user1@example.com",
|
||||
isSuperuser: true,
|
||||
});
|
||||
const user2 = await createTestUser(db, {
|
||||
email: "user2@example.com",
|
||||
isSuperuser: true,
|
||||
});
|
||||
|
||||
const tokenHash1 = await hashToken("token1");
|
||||
const tokenHash2 = await hashToken("token2");
|
||||
@@ -1650,23 +1670,35 @@ describeE2E("me.apiTokens and me.invites", () => {
|
||||
await db
|
||||
.insertInto("api_tokens")
|
||||
.values([
|
||||
{ user_id: user1.id, token_hash: tokenHash1, name: "User1 Token", expires_at: expiresAt },
|
||||
{ user_id: user2.id, token_hash: tokenHash2, name: "User2 Token", expires_at: expiresAt },
|
||||
{
|
||||
user_id: user1.id,
|
||||
token_hash: tokenHash1,
|
||||
name: "User1 Token",
|
||||
expires_at: expiresAt,
|
||||
},
|
||||
{
|
||||
user_id: user2.id,
|
||||
token_hash: tokenHash2,
|
||||
name: "User2 Token",
|
||||
expires_at: expiresAt,
|
||||
},
|
||||
])
|
||||
.execute();
|
||||
|
||||
const { token: sessionToken } = await createSession(db, user1.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, { context });
|
||||
const tokens = await call(router.me.apiTokens.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(tokens).toHaveLength(1);
|
||||
expect(tokens[0].name).toBe("User1 Token");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("me.apiTokens.create", () => {
|
||||
describe("me.apiTokens.create", () => {
|
||||
test("creates token for superuser with trusted session", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const user = await createTestUser(db, {
|
||||
@@ -1731,9 +1763,9 @@ describe("me.apiTokens.create", () => {
|
||||
).rejects.toThrow("Creating API tokens requires a trusted session");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("me.apiTokens.delete", () => {
|
||||
describe("me.apiTokens.delete", () => {
|
||||
test("deletes own token", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const user = await createTestUser(db, {
|
||||
@@ -1746,7 +1778,12 @@ describe("me.apiTokens.delete", () => {
|
||||
|
||||
const insertResult = await db
|
||||
.insertInto("api_tokens")
|
||||
.values({ user_id: user.id, token_hash: tokenHash, name: "To Delete", expires_at: expiresAt })
|
||||
.values({
|
||||
user_id: user.id,
|
||||
token_hash: tokenHash,
|
||||
name: "To Delete",
|
||||
expires_at: expiresAt,
|
||||
})
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
@@ -1774,15 +1811,26 @@ describe("me.apiTokens.delete", () => {
|
||||
|
||||
test("cannot delete other user token", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const user1 = await createTestUser(db, { email: "owner@example.com", isSuperuser: true });
|
||||
const user2 = await createTestUser(db, { email: "other@example.com", isSuperuser: true });
|
||||
const user1 = await createTestUser(db, {
|
||||
email: "owner@example.com",
|
||||
isSuperuser: true,
|
||||
});
|
||||
const user2 = await createTestUser(db, {
|
||||
email: "other@example.com",
|
||||
isSuperuser: true,
|
||||
});
|
||||
|
||||
const tokenHash = await hashToken("other-token");
|
||||
const expiresAt = new Date(Date.now() + API_TOKEN_EXPIRY_MS);
|
||||
|
||||
const insertResult = await db
|
||||
.insertInto("api_tokens")
|
||||
.values({ user_id: user1.id, token_hash: tokenHash, name: "User1 Token", expires_at: expiresAt })
|
||||
.values({
|
||||
user_id: user1.id,
|
||||
token_hash: tokenHash,
|
||||
name: "User1 Token",
|
||||
expires_at: expiresAt,
|
||||
})
|
||||
.returning("id")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
@@ -1790,7 +1838,11 @@ describe("me.apiTokens.delete", () => {
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
await expect(
|
||||
call(router.me.apiTokens.delete, { tokenId: Number(insertResult.id) }, { context }),
|
||||
call(
|
||||
router.me.apiTokens.delete,
|
||||
{ tokenId: Number(insertResult.id) },
|
||||
{ context },
|
||||
),
|
||||
).rejects.toThrow("API token not found");
|
||||
});
|
||||
});
|
||||
@@ -1810,24 +1862,29 @@ describe("me.apiTokens.delete", () => {
|
||||
).rejects.toThrow("API token not found");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// =============================================================================
|
||||
// me.invites tests
|
||||
// =============================================================================
|
||||
// =============================================================================
|
||||
// me.invites tests
|
||||
// =============================================================================
|
||||
|
||||
describe("me.invites.list", () => {
|
||||
describe("me.invites.list", () => {
|
||||
test("returns empty list when email not verified", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const inviter = await createTestUser(db, {
|
||||
email: "inviter@example.com",
|
||||
emailVerifiedAt: new Date(),
|
||||
});
|
||||
const org = await createOrg(db, { slug: "test-org", displayName: "Test Org" });
|
||||
const org = await createOrg(db, {
|
||||
slug: "test-org",
|
||||
displayName: "Test Org",
|
||||
});
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
// User without verified email
|
||||
const user = await createTestUser(db, { email: "unverified@example.com" });
|
||||
const user = await createTestUser(db, {
|
||||
email: "unverified@example.com",
|
||||
});
|
||||
|
||||
// Create an invite for the unverified user
|
||||
await createOrgInvite(db, {
|
||||
@@ -1839,7 +1896,9 @@ describe("me.invites.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, user.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const invites = await call(router.me.invites.list, undefined, { context });
|
||||
const invites = await call(router.me.invites.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(invites).toHaveLength(0);
|
||||
});
|
||||
@@ -1852,7 +1911,10 @@ describe("me.invites.list", () => {
|
||||
emailVerifiedAt: new Date(),
|
||||
displayName: "Inviter Person",
|
||||
});
|
||||
const org = await createOrg(db, { slug: "invite-org", displayName: "Invite Org" });
|
||||
const org = await createOrg(db, {
|
||||
slug: "invite-org",
|
||||
displayName: "Invite Org",
|
||||
});
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
const user = await createTestUser(db, {
|
||||
@@ -1870,7 +1932,9 @@ describe("me.invites.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, user.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const invites = await call(router.me.invites.list, undefined, { context });
|
||||
const invites = await call(router.me.invites.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(invites).toHaveLength(1);
|
||||
expect(invites[0].org.slug).toBe("invite-org");
|
||||
@@ -1905,14 +1969,16 @@ describe("me.invites.list", () => {
|
||||
const { token: sessionToken } = await createSession(db, user.id);
|
||||
const context = createAPIContext(db, { sessionToken });
|
||||
|
||||
const invites = await call(router.me.invites.list, undefined, { context });
|
||||
const invites = await call(router.me.invites.list, undefined, {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(invites).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("me.invites.get", () => {
|
||||
describe("me.invites.get", () => {
|
||||
test("returns invite details", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const inviter = await createTestUser(db, {
|
||||
@@ -1920,7 +1986,10 @@ describe("me.invites.get", () => {
|
||||
emailVerifiedAt: new Date(),
|
||||
displayName: "The Inviter",
|
||||
});
|
||||
const org = await createOrg(db, { slug: "get-invite-org", displayName: "Get Invite Org" });
|
||||
const org = await createOrg(db, {
|
||||
slug: "get-invite-org",
|
||||
displayName: "Get Invite Org",
|
||||
});
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
const user = await createTestUser(db, {
|
||||
@@ -1960,7 +2029,9 @@ describe("me.invites.get", () => {
|
||||
const org = await createOrg(db, { slug: "unverified-get-org" });
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
const user = await createTestUser(db, { email: "unverified2@example.com" });
|
||||
const user = await createTestUser(db, {
|
||||
email: "unverified2@example.com",
|
||||
});
|
||||
|
||||
const invite = await createOrgInvite(db, {
|
||||
orgId: org.id,
|
||||
@@ -2010,12 +2081,12 @@ describe("me.invites.get", () => {
|
||||
).rejects.toThrow("Invitation not found or expired");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("me.invites.accept", () => {
|
||||
describe("me.invites.accept", () => {
|
||||
test("accepts invite and adds user to org", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const inviter = await createTestUser(db, {
|
||||
email: `inviter-accept-${uniqueId}@example.com`,
|
||||
@@ -2069,9 +2140,18 @@ describe("me.invites.accept", () => {
|
||||
expect(inviteCheck).toBeUndefined();
|
||||
} finally {
|
||||
// Cleanup
|
||||
await db.deleteFrom("org_members").where("org_id", "=", org.id).execute();
|
||||
await db.deleteFrom("org_invites").where("org_id", "=", org.id).execute();
|
||||
await db.deleteFrom("sessions").where("user_id", "=", user.id).execute();
|
||||
await db
|
||||
.deleteFrom("org_members")
|
||||
.where("org_id", "=", org.id)
|
||||
.execute();
|
||||
await db
|
||||
.deleteFrom("org_invites")
|
||||
.where("org_id", "=", org.id)
|
||||
.execute();
|
||||
await db
|
||||
.deleteFrom("sessions")
|
||||
.where("user_id", "=", user.id)
|
||||
.execute();
|
||||
await db.deleteFrom("orgs").where("id", "=", org.id).execute();
|
||||
await db.deleteFrom("users").where("id", "=", user.id).execute();
|
||||
await db.deleteFrom("users").where("id", "=", inviter.id).execute();
|
||||
@@ -2087,7 +2167,9 @@ describe("me.invites.accept", () => {
|
||||
const org = await createOrg(db, { slug: "unverified-accept-org" });
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
const user = await createTestUser(db, { email: "unverified3@example.com" });
|
||||
const user = await createTestUser(db, {
|
||||
email: "unverified3@example.com",
|
||||
});
|
||||
|
||||
const invite = await createOrgInvite(db, {
|
||||
orgId: org.id,
|
||||
@@ -2106,13 +2188,15 @@ describe("me.invites.accept", () => {
|
||||
|
||||
test("returns error if already a member", async () => {
|
||||
const db = getSharedDb();
|
||||
const uniqueId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
const uniqueId = `${String(Date.now())}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
|
||||
const inviter = await createTestUser(db, {
|
||||
email: `inviter-already-${uniqueId}@example.com`,
|
||||
emailVerifiedAt: new Date(),
|
||||
});
|
||||
const org = await createOrg(db, { slug: `already-member-org-${uniqueId}` });
|
||||
const org = await createOrg(db, {
|
||||
slug: `already-member-org-${uniqueId}`,
|
||||
});
|
||||
await addOrgMember(db, org.id, inviter.id, "owner");
|
||||
|
||||
const user = await createTestUser(db, {
|
||||
@@ -2139,9 +2223,18 @@ describe("me.invites.accept", () => {
|
||||
).rejects.toThrow("You are already a member of this organization");
|
||||
} finally {
|
||||
// Cleanup
|
||||
await db.deleteFrom("org_members").where("org_id", "=", org.id).execute();
|
||||
await db.deleteFrom("org_invites").where("org_id", "=", org.id).execute();
|
||||
await db.deleteFrom("sessions").where("user_id", "=", user.id).execute();
|
||||
await db
|
||||
.deleteFrom("org_members")
|
||||
.where("org_id", "=", org.id)
|
||||
.execute();
|
||||
await db
|
||||
.deleteFrom("org_invites")
|
||||
.where("org_id", "=", org.id)
|
||||
.execute();
|
||||
await db
|
||||
.deleteFrom("sessions")
|
||||
.where("user_id", "=", user.id)
|
||||
.execute();
|
||||
await db.deleteFrom("orgs").where("id", "=", org.id).execute();
|
||||
await db.deleteFrom("users").where("id", "=", user.id).execute();
|
||||
await db.deleteFrom("users").where("id", "=", inviter.id).execute();
|
||||
@@ -2163,9 +2256,9 @@ describe("me.invites.accept", () => {
|
||||
).rejects.toThrow("Invitation not found or expired");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("me.invites.decline", () => {
|
||||
describe("me.invites.decline", () => {
|
||||
test("declines invite and deletes it", async () => {
|
||||
await withTestTransaction(getSharedDb(), async (db) => {
|
||||
const inviter = await createTestUser(db, {
|
||||
@@ -2256,4 +2349,5 @@ describe("me.invites.decline", () => {
|
||||
).rejects.toThrow("Invitation not found");
|
||||
});
|
||||
});
|
||||
}); // Close describe for me.invites.decline
|
||||
}); // Close describeE2E for me.apiTokens and me.invites
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -68,7 +68,9 @@ export async function signupWithPassword(
|
||||
// Handle duplicate email (unique constraint violation)
|
||||
// Use generic error to prevent email enumeration
|
||||
if (error instanceof Error && error.message.includes("users_email_key")) {
|
||||
throw new ORPCError("BAD_REQUEST", { message: "Unable to create account" });
|
||||
throw new ORPCError("BAD_REQUEST", {
|
||||
message: "Unable to create account",
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -209,7 +211,9 @@ export async function signupWithPasskey(
|
||||
// Handle duplicate email (unique constraint violation)
|
||||
// Use generic error to prevent email enumeration
|
||||
if (error instanceof Error && error.message.includes("users_email_key")) {
|
||||
throw new ORPCError("BAD_REQUEST", { message: "Unable to create account" });
|
||||
throw new ORPCError("BAD_REQUEST", {
|
||||
message: "Unable to create account",
|
||||
});
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -115,11 +115,11 @@ export async function countOwners(
|
||||
): Promise<number> {
|
||||
const result = await db
|
||||
.selectFrom("org_members")
|
||||
.select((eb) => eb.fn.countAll<number>().as("count"))
|
||||
.select((eb) => eb.fn.countAll().as("count"))
|
||||
.where("org_id", "=", orgId)
|
||||
.where("role", "=", "owner")
|
||||
.executeTakeFirstOrThrow();
|
||||
|
||||
// PostgreSQL COUNT returns bigint which may be a string; ensure numeric comparison works
|
||||
// PostgreSQL COUNT returns bigint (string), convert to number
|
||||
return Number(result.count);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
\restrict 7omiXDURqmmr2m2jWDDMoltRzeUAT80fRWiPifpD7IpQGCLgxQNBFsA5uBgakPg
|
||||
\restrict NNYnwssF6iMx0TXsk1nTprUEDwxna9uejAmsIiUlMLcPLlQlnnRVCusYtzweHXM
|
||||
|
||||
-- Dumped from database version 17.7
|
||||
-- Dumped by pg_dump version 17.7
|
||||
@@ -1084,7 +1084,7 @@ ALTER TABLE ONLY public.user_devices
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
\unrestrict 7omiXDURqmmr2m2jWDDMoltRzeUAT80fRWiPifpD7IpQGCLgxQNBFsA5uBgakPg
|
||||
\unrestrict NNYnwssF6iMx0TXsk1nTprUEDwxna9uejAmsIiUlMLcPLlQlnnRVCusYtzweHXM
|
||||
|
||||
|
||||
--
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ruleDirs:
|
||||
- /Users/igm/proj/reviq/publisher-dashboard/.ast-grep/rules/
|
||||
- .ast-grep/rules/
|
||||
testConfigs:
|
||||
- testDir: /Users/igm/proj/reviq/publisher-dashboard/.ast-grep/rule-tests/
|
||||
- testDir: .ast-grep/rule-tests/
|
||||
utilDirs:
|
||||
- /Users/igm/proj/reviq/publisher-dashboard/.ast-grep/utils/
|
||||
- .ast-grep/utils/
|
||||
|
||||
Reference in New Issue
Block a user