mirror of
https://github.com/kennethnym/aris.git
synced 2026-06-16 12:31:17 +01:00
Compare commits
1 Commits
feat/defau
...
feat/remin
| Author | SHA1 | Date | |
|---|---|---|---|
|
f291495d0c
|
@@ -1,83 +0,0 @@
|
|||||||
import { afterEach, describe, expect, test } from "bun:test"
|
|
||||||
|
|
||||||
import type { Database } from "../db/index.ts"
|
|
||||||
|
|
||||||
import { DEFAULT_ENABLED_SOURCE_IDS } from "../sources/default-sources.ts"
|
|
||||||
import { createAuth } from "./index.ts"
|
|
||||||
|
|
||||||
interface UserSourceInsertRow {
|
|
||||||
sourceId: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RecordingDb {
|
|
||||||
db: Database
|
|
||||||
rows: () => UserSourceInsertRow[] | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalBetterAuthSecret = process.env.BETTER_AUTH_SECRET
|
|
||||||
|
|
||||||
function createRecordingDb(): RecordingDb {
|
|
||||||
let insertedRows: UserSourceInsertRow[] | undefined
|
|
||||||
|
|
||||||
const db = {
|
|
||||||
insert() {
|
|
||||||
return {
|
|
||||||
values(rows: UserSourceInsertRow[]) {
|
|
||||||
insertedRows = rows
|
|
||||||
|
|
||||||
return {
|
|
||||||
async onConflictDoNothing() {},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
} as unknown as Database
|
|
||||||
|
|
||||||
return {
|
|
||||||
db,
|
|
||||||
rows: () => insertedRows,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
if (originalBetterAuthSecret === undefined) {
|
|
||||||
delete process.env.BETTER_AUTH_SECRET
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
process.env.BETTER_AUTH_SECRET = originalBetterAuthSecret
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("createAuth", () => {
|
|
||||||
test("inserts default sources after Better Auth creates a user", async () => {
|
|
||||||
process.env.BETTER_AUTH_SECRET = "test-secret"
|
|
||||||
const recording = createRecordingDb()
|
|
||||||
const auth = createAuth(recording.db)
|
|
||||||
const afterCreateUser = auth.options.databaseHooks?.user?.create?.after
|
|
||||||
|
|
||||||
if (!afterCreateUser) {
|
|
||||||
throw new Error("Expected a user create after hook")
|
|
||||||
}
|
|
||||||
|
|
||||||
const now = new Date()
|
|
||||||
await afterCreateUser(
|
|
||||||
{
|
|
||||||
id: "user-1",
|
|
||||||
name: "Test User",
|
|
||||||
email: "test@example.com",
|
|
||||||
emailVerified: false,
|
|
||||||
image: null,
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now,
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
|
|
||||||
const rows = recording.rows()
|
|
||||||
if (!rows) {
|
|
||||||
throw new Error("Expected the auth hook to insert default sources")
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(rows.map((row) => row.sourceId)).toEqual([...DEFAULT_ENABLED_SOURCE_IDS])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -5,7 +5,6 @@ import { admin } from "better-auth/plugins"
|
|||||||
import type { Database } from "../db/index.ts"
|
import type { Database } from "../db/index.ts"
|
||||||
|
|
||||||
import * as schema from "../db/schema.ts"
|
import * as schema from "../db/schema.ts"
|
||||||
import { insertDefaultUserSources } from "../sources/default-sources.ts"
|
|
||||||
|
|
||||||
export function createAuth(db: Database) {
|
export function createAuth(db: Database) {
|
||||||
if (!process.env.BETTER_AUTH_SECRET) {
|
if (!process.env.BETTER_AUTH_SECRET) {
|
||||||
@@ -23,15 +22,6 @@ export function createAuth(db: Database) {
|
|||||||
emailAndPassword: {
|
emailAndPassword: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
databaseHooks: {
|
|
||||||
user: {
|
|
||||||
create: {
|
|
||||||
async after(user, _context) {
|
|
||||||
await insertDefaultUserSources(db, user.id)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [admin()],
|
plugins: [admin()],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { LocationSource } from "@freya/source-location"
|
|||||||
import type { FeedSourceProvider } from "../session/feed-source-provider.ts"
|
import type { FeedSourceProvider } from "../session/feed-source-provider.ts"
|
||||||
|
|
||||||
export class LocationSourceProvider implements FeedSourceProvider {
|
export class LocationSourceProvider implements FeedSourceProvider {
|
||||||
readonly sourceId = LocationSource.id
|
readonly sourceId = "freya.location"
|
||||||
|
|
||||||
async feedSourceForUser(
|
async feedSourceForUser(
|
||||||
_userId: string,
|
_userId: string,
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
import { LocationSource } from "@freya/source-location"
|
|
||||||
import { WebSearchSource } from "@freya/source-web-search"
|
|
||||||
import { describe, expect, test } from "bun:test"
|
|
||||||
|
|
||||||
import type { Database } from "../db/index.ts"
|
|
||||||
|
|
||||||
import { userSources } from "../db/schema.ts"
|
|
||||||
import { DEFAULT_ENABLED_SOURCE_IDS, insertDefaultUserSources } from "./default-sources.ts"
|
|
||||||
|
|
||||||
interface UserSourceInsertRow {
|
|
||||||
userId: string
|
|
||||||
sourceId: string
|
|
||||||
enabled: boolean
|
|
||||||
config: unknown
|
|
||||||
createdAt: Date
|
|
||||||
updatedAt: Date
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RecordingDb {
|
|
||||||
db: Database
|
|
||||||
table: () => unknown
|
|
||||||
rows: () => UserSourceInsertRow[] | undefined
|
|
||||||
conflictTarget: () => readonly unknown[] | undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRecordingDb(): RecordingDb {
|
|
||||||
let insertedTable: unknown
|
|
||||||
let insertedRows: UserSourceInsertRow[] | undefined
|
|
||||||
let target: readonly unknown[] | undefined
|
|
||||||
|
|
||||||
const db = {
|
|
||||||
insert(table: unknown) {
|
|
||||||
insertedTable = table
|
|
||||||
|
|
||||||
return {
|
|
||||||
values(rows: UserSourceInsertRow[]) {
|
|
||||||
insertedRows = rows
|
|
||||||
|
|
||||||
return {
|
|
||||||
async onConflictDoNothing(options: { target: readonly unknown[] }) {
|
|
||||||
target = options.target
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
} as unknown as Database
|
|
||||||
|
|
||||||
return {
|
|
||||||
db,
|
|
||||||
table: () => insertedTable,
|
|
||||||
rows: () => insertedRows,
|
|
||||||
conflictTarget: () => target,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("default user sources", () => {
|
|
||||||
test("defines location and web search as default enabled sources", () => {
|
|
||||||
expect(DEFAULT_ENABLED_SOURCE_IDS).toEqual([LocationSource.id, WebSearchSource.id])
|
|
||||||
})
|
|
||||||
|
|
||||||
test("inserts default enabled source rows for a user", async () => {
|
|
||||||
const recording = createRecordingDb()
|
|
||||||
|
|
||||||
await insertDefaultUserSources(recording.db, "user-1")
|
|
||||||
|
|
||||||
const rows = recording.rows()
|
|
||||||
if (!rows) {
|
|
||||||
throw new Error("Expected default source rows to be inserted")
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(recording.table()).toBe(userSources)
|
|
||||||
expect(rows).toHaveLength(2)
|
|
||||||
expect(rows.map((row) => row.sourceId)).toEqual([...DEFAULT_ENABLED_SOURCE_IDS])
|
|
||||||
expect(recording.conflictTarget()).toEqual([userSources.userId, userSources.sourceId])
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
expect(row.userId).toBe("user-1")
|
|
||||||
expect(row.enabled).toBe(true)
|
|
||||||
expect(row.config).toEqual({})
|
|
||||||
expect(row.createdAt).toBeInstanceOf(Date)
|
|
||||||
expect(row.updatedAt).toBe(row.createdAt)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { LocationSource } from "@freya/source-location"
|
|
||||||
import { WebSearchSource } from "@freya/source-web-search"
|
|
||||||
|
|
||||||
import type { Database } from "../db/index.ts"
|
|
||||||
|
|
||||||
import { userSources } from "../db/schema.ts"
|
|
||||||
|
|
||||||
export const DEFAULT_ENABLED_SOURCE_IDS = [LocationSource.id, WebSearchSource.id] as const
|
|
||||||
|
|
||||||
export type DefaultEnabledSourceId = (typeof DEFAULT_ENABLED_SOURCE_IDS)[number]
|
|
||||||
|
|
||||||
export async function insertDefaultUserSources(db: Database, userId: string): Promise<void> {
|
|
||||||
const now = new Date()
|
|
||||||
|
|
||||||
await db
|
|
||||||
.insert(userSources)
|
|
||||||
.values(
|
|
||||||
DEFAULT_ENABLED_SOURCE_IDS.map((sourceId) => ({
|
|
||||||
userId,
|
|
||||||
sourceId,
|
|
||||||
enabled: true,
|
|
||||||
config: {},
|
|
||||||
createdAt: now,
|
|
||||||
updatedAt: now,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.onConflictDoNothing({
|
|
||||||
target: [userSources.userId, userSources.sourceId],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ export type WebSearchSourceProviderOptions =
|
|||||||
| { apiKey?: never; client: WebSearchClient }
|
| { apiKey?: never; client: WebSearchClient }
|
||||||
|
|
||||||
export class WebSearchSourceProvider implements FeedSourceProvider {
|
export class WebSearchSourceProvider implements FeedSourceProvider {
|
||||||
readonly sourceId = WebSearchSource.id
|
readonly sourceId = "freya.web-search"
|
||||||
|
|
||||||
private readonly apiKey: string | undefined
|
private readonly apiKey: string | undefined
|
||||||
private readonly client: WebSearchClient | undefined
|
private readonly client: WebSearchClient | undefined
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe("LocationSource", () => {
|
|||||||
describe("FeedSource interface", () => {
|
describe("FeedSource interface", () => {
|
||||||
test("has correct id", () => {
|
test("has correct id", () => {
|
||||||
const source = new LocationSource()
|
const source = new LocationSource()
|
||||||
expect(source.id).toBe(LocationSource.id)
|
expect(source.id).toBe("freya.location")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("fetchItems always returns empty array", async () => {
|
test("fetchItems always returns empty array", async () => {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { type } from "arktype"
|
|||||||
|
|
||||||
import { Location, type LocationSourceOptions } from "./types.ts"
|
import { Location, type LocationSourceOptions } from "./types.ts"
|
||||||
|
|
||||||
|
export const LocationKey: ContextKey<Location> = contextKey("freya.location", "location")
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A FeedSource that provides location context.
|
* A FeedSource that provides location context.
|
||||||
*
|
*
|
||||||
@@ -14,9 +16,7 @@ import { Location, type LocationSourceOptions } from "./types.ts"
|
|||||||
* Does not produce feed items - always returns empty array from `fetchItems`.
|
* Does not produce feed items - always returns empty array from `fetchItems`.
|
||||||
*/
|
*/
|
||||||
export class LocationSource implements FeedSource {
|
export class LocationSource implements FeedSource {
|
||||||
static readonly id = "freya.location"
|
readonly id = "freya.location"
|
||||||
|
|
||||||
readonly id = LocationSource.id
|
|
||||||
|
|
||||||
private readonly historySize: number
|
private readonly historySize: number
|
||||||
private locations: Location[] = []
|
private locations: Location[] = []
|
||||||
@@ -97,5 +97,3 @@ export class LocationSource implements FeedSource {
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LocationKey: ContextKey<Location> = contextKey(LocationSource.id, "location")
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe("WebSearchSource", () => {
|
|||||||
test("has correct id", () => {
|
test("has correct id", () => {
|
||||||
const source = new WebSearchSource({ client: new RecordingSearchClient() })
|
const source = new WebSearchSource({ client: new RecordingSearchClient() })
|
||||||
|
|
||||||
expect(source.id).toBe(WebSearchSource.id)
|
expect(source.id).toBe("freya.web-search")
|
||||||
})
|
})
|
||||||
|
|
||||||
test("does not provide context or feed items", async () => {
|
test("does not provide context or feed items", async () => {
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ const SearchInput = type({
|
|||||||
* action and receive structured web results.
|
* action and receive structured web results.
|
||||||
*/
|
*/
|
||||||
export class WebSearchSource implements FeedSource {
|
export class WebSearchSource implements FeedSource {
|
||||||
static readonly id = "freya.web-search"
|
readonly id = "freya.web-search"
|
||||||
|
|
||||||
readonly id = WebSearchSource.id
|
|
||||||
|
|
||||||
private readonly client: WebSearchClient
|
private readonly client: WebSearchClient
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user