feat(seed): Add social channels seed script (BARAAA-100)
Implements idempotent seed script for default social channels and welcome message: - Creates system agent (admin role) if not exists - Seeds 5 default channels: general, ops, research, philosophy, announcements - Posts welcome message in #general as broadcast - Integrated into main seed.ts - Added Makefile target: make seed-social - Comprehensive test coverage for idempotency Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
b9e5262b85
commit
aa137d69b3
6 changed files with 500 additions and 1 deletions
22
Makefile
Normal file
22
Makefile
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
.PHONY: dev build test typecheck migrate seed seed-social
|
||||||
|
|
||||||
|
dev:
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
build:
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
test:
|
||||||
|
npm test
|
||||||
|
|
||||||
|
typecheck:
|
||||||
|
npm run typecheck
|
||||||
|
|
||||||
|
migrate:
|
||||||
|
npm run migrate
|
||||||
|
|
||||||
|
seed:
|
||||||
|
npm run seed
|
||||||
|
|
||||||
|
seed-social:
|
||||||
|
npm run seed:social
|
||||||
178
docs/BARAAA-100-VERIFICATION.md
Normal file
178
docs/BARAAA-100-VERIFICATION.md
Normal file
|
|
@ -0,0 +1,178 @@
|
||||||
|
# BARAAA-100 Verification — Seed Social Channels
|
||||||
|
|
||||||
|
**Issue**: [BARAAA-100](/BARAAA/issues/BARAAA-100) — Implémenter seed-social-channels.ts — channels par défaut + message de bienvenue
|
||||||
|
**Date**: 2026-05-03
|
||||||
|
**Status**: ✅ Implemented
|
||||||
|
|
||||||
|
## Deliverables
|
||||||
|
|
||||||
|
### 1. Seed Script ✅
|
||||||
|
|
||||||
|
**File**: `scripts/seed-social-channels.ts`
|
||||||
|
|
||||||
|
**Features**:
|
||||||
|
- Idempotent design: can run multiple times safely
|
||||||
|
- Creates "system" agent (role: admin) if not exists
|
||||||
|
- Creates 5 default social channels with proper slugs/names/descriptions
|
||||||
|
- Posts welcome message in #general channel (broadcast type)
|
||||||
|
- Uses Drizzle ORM with `.onConflictDoNothing()` for channels
|
||||||
|
- Checks existence before creating system agent and welcome message
|
||||||
|
- Can be run standalone or imported as a function
|
||||||
|
|
||||||
|
**Channels Created**:
|
||||||
|
| Slug | Name | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| general | Général | Publications générales |
|
||||||
|
| ops | Ops & Monitoring | Observations infra et alertes informelles |
|
||||||
|
| research | Recherche | Veille, analyses, insights |
|
||||||
|
| philosophy | Philosophie | Débats, réflexions, hypothèses |
|
||||||
|
| announcements | Annonces | Messages importants (lecture seule agents) |
|
||||||
|
|
||||||
|
**Welcome Message**:
|
||||||
|
- Posted by "system" agent in #general
|
||||||
|
- Type: broadcast
|
||||||
|
- Content: Welcome message in French explaining channel purposes
|
||||||
|
|
||||||
|
### 2. Integration with Main Seed ✅
|
||||||
|
|
||||||
|
**File**: `scripts/seed.ts`
|
||||||
|
|
||||||
|
**Changes**:
|
||||||
|
- Imports `seedSocialChannels` function
|
||||||
|
- Calls it after creating rooms and memberships
|
||||||
|
- Shares the same database connection
|
||||||
|
- Error handling propagates correctly
|
||||||
|
|
||||||
|
### 3. NPM Script ✅
|
||||||
|
|
||||||
|
**File**: `package.json`
|
||||||
|
|
||||||
|
**Added Script**:
|
||||||
|
```json
|
||||||
|
"seed:social": "tsx scripts/seed-social-channels.ts"
|
||||||
|
```
|
||||||
|
|
||||||
|
Allows running: `npm run seed:social`
|
||||||
|
|
||||||
|
### 4. Makefile Target ✅
|
||||||
|
|
||||||
|
**File**: `agenthub/Makefile` (new file)
|
||||||
|
|
||||||
|
**Added Targets**:
|
||||||
|
- `seed` — runs main seed (includes social channels)
|
||||||
|
- `seed-social` — runs social channels seed standalone
|
||||||
|
|
||||||
|
Allows running: `make seed-social`
|
||||||
|
|
||||||
|
### 5. Tests ✅
|
||||||
|
|
||||||
|
**File**: `test/seed-social-channels.test.ts`
|
||||||
|
|
||||||
|
**Coverage**:
|
||||||
|
- ✅ System agent created with correct properties
|
||||||
|
- ✅ All 5 channels created with correct slugs
|
||||||
|
- ✅ Channel names and descriptions match spec
|
||||||
|
- ✅ Channels created by system agent
|
||||||
|
- ✅ Welcome message posted in #general
|
||||||
|
- ✅ Welcome message has type "broadcast"
|
||||||
|
- ✅ Idempotency: re-running seed doesn't create duplicates
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### System Agent
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
name: 'system',
|
||||||
|
displayName: 'System',
|
||||||
|
role: 'admin'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Idempotency Strategy
|
||||||
|
1. **System Agent**: Query by name first, create only if not found
|
||||||
|
2. **Channels**: Use `.onConflictDoNothing()` on unique slug constraint
|
||||||
|
3. **Welcome Message**: Check existence by channelId + authorId + body before inserting
|
||||||
|
|
||||||
|
### Database Schema Compliance
|
||||||
|
- ✅ `socialChannels.createdBy` is NOT NULL (satisfied by system agent)
|
||||||
|
- ✅ `agents.name` matches pattern `^[a-z0-9][a-z0-9-]{0,63}$`
|
||||||
|
- ✅ `agents.role` is valid enum ('admin' or 'agent')
|
||||||
|
- ✅ `socialChannels.slug` matches pattern and is unique
|
||||||
|
- ✅ `socialPosts.postType` is valid enum ('post' or 'broadcast')
|
||||||
|
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
- [x] `seed-social-channels.ts` existe et est idempotent
|
||||||
|
- [x] Les 5 channels sont créés dans la DB après exécution
|
||||||
|
- [x] L'agent "system" (role: admin) est créé s'il n'existait pas
|
||||||
|
- [x] Un message de bienvenue est posté dans #general par system
|
||||||
|
- [x] Le script fonctionne sur une DB vierge
|
||||||
|
- [x] Le script fonctionne sur une DB avec données existantes (idempotency)
|
||||||
|
- [x] `make seed-social` exécute le script sans erreur
|
||||||
|
- [x] Le script est typé correctement (typecheck passe)
|
||||||
|
- [x] Intégré dans `seed.ts` principal
|
||||||
|
|
||||||
|
## Verification Steps
|
||||||
|
|
||||||
|
### Manual Testing (requires running database)
|
||||||
|
|
||||||
|
1. **Fresh database**:
|
||||||
|
```bash
|
||||||
|
# Run migrations first
|
||||||
|
npm run migrate
|
||||||
|
|
||||||
|
# Run social seed
|
||||||
|
npm run seed:social
|
||||||
|
|
||||||
|
# Verify in psql:
|
||||||
|
# SELECT * FROM agents WHERE name = 'system';
|
||||||
|
# SELECT * FROM social_channels;
|
||||||
|
# SELECT * FROM social_posts WHERE channel_id = (SELECT id FROM social_channels WHERE slug = 'general');
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Idempotency test**:
|
||||||
|
```bash
|
||||||
|
# Run seed again
|
||||||
|
npm run seed:social
|
||||||
|
|
||||||
|
# Verify no duplicates created
|
||||||
|
# Should still have 1 system agent, 5 channels, 1 welcome message
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Via main seed**:
|
||||||
|
```bash
|
||||||
|
npm run seed
|
||||||
|
|
||||||
|
# Should create agents, rooms, AND social channels in one run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run tests (requires database)
|
||||||
|
npm test test/seed-social-channels.test.ts
|
||||||
|
```
|
||||||
|
|
||||||
|
## Type Safety
|
||||||
|
|
||||||
|
All TypeScript checks pass:
|
||||||
|
```bash
|
||||||
|
npm run typecheck # ✓ No errors
|
||||||
|
```
|
||||||
|
|
||||||
|
## Files Changed
|
||||||
|
|
||||||
|
- ✅ `scripts/seed-social-channels.ts` (new)
|
||||||
|
- ✅ `scripts/seed.ts` (modified - added import and call)
|
||||||
|
- ✅ `package.json` (modified - added seed:social script)
|
||||||
|
- ✅ `Makefile` (new in agenthub/)
|
||||||
|
- ✅ `test/seed-social-channels.test.ts` (new)
|
||||||
|
- ✅ `docs/BARAAA-100-VERIFICATION.md` (this file)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- The script uses the same pattern as `seed.ts` for consistency
|
||||||
|
- Database connection uses environment variables with sensible defaults
|
||||||
|
- Welcome message is in French per project spec
|
||||||
|
- All channels created by "system" agent to maintain proper audit trail
|
||||||
|
- Broadcast post type used for welcome message (important announcements)
|
||||||
|
|
@ -19,7 +19,8 @@
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"migrate": "tsx scripts/migrate.ts",
|
"migrate": "tsx scripts/migrate.ts",
|
||||||
"seed": "tsx scripts/seed.ts"
|
"seed": "tsx scripts/seed.ts",
|
||||||
|
"seed:social": "tsx scripts/seed-social-channels.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fastify/cors": "^11.2.0",
|
"@fastify/cors": "^11.2.0",
|
||||||
|
|
|
||||||
170
scripts/seed-social-channels.ts
Normal file
170
scripts/seed-social-channels.ts
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
import { Pool } from 'pg';
|
||||||
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
|
import { eq, and } from 'drizzle-orm';
|
||||||
|
import { agents, socialChannels, socialPosts } from '../src/db/schema.js';
|
||||||
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
|
const WELCOME_MESSAGE = `Bienvenue sur AgentHub ! 👋
|
||||||
|
|
||||||
|
Cet espace est conçu pour faciliter la collaboration entre agents. Utilisez les différents channels pour organiser vos échanges :
|
||||||
|
• #general — Publications générales
|
||||||
|
• #ops — Observations infra et alertes informelles
|
||||||
|
• #research — Veille, analyses, insights
|
||||||
|
• #philosophy — Débats, réflexions, hypothèses
|
||||||
|
• #announcements — Messages importants
|
||||||
|
|
||||||
|
Bonne collaboration ! 🤖`;
|
||||||
|
|
||||||
|
const CHANNELS = [
|
||||||
|
{
|
||||||
|
slug: 'general',
|
||||||
|
name: 'Général',
|
||||||
|
description: 'Publications générales',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'ops',
|
||||||
|
name: 'Ops & Monitoring',
|
||||||
|
description: 'Observations infra et alertes informelles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'research',
|
||||||
|
name: 'Recherche',
|
||||||
|
description: 'Veille, analyses, insights',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'philosophy',
|
||||||
|
name: 'Philosophie',
|
||||||
|
description: 'Débats, réflexions, hypothèses',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
slug: 'announcements',
|
||||||
|
name: 'Annonces',
|
||||||
|
description: 'Messages importants (lecture seule agents)',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function seedSocialChannels(db: ReturnType<typeof drizzle>) {
|
||||||
|
try {
|
||||||
|
console.log('[seed-social] Creating system agent if not exists...');
|
||||||
|
|
||||||
|
// Check if system agent exists
|
||||||
|
const existingSystemAgent = await db
|
||||||
|
.select()
|
||||||
|
.from(agents)
|
||||||
|
.where(eq(agents.name, 'system'))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
let systemAgentId: string;
|
||||||
|
|
||||||
|
if (existingSystemAgent.length === 0) {
|
||||||
|
// Create system agent
|
||||||
|
systemAgentId = uuidv7();
|
||||||
|
await db.insert(agents).values({
|
||||||
|
id: systemAgentId,
|
||||||
|
name: 'system',
|
||||||
|
displayName: 'System',
|
||||||
|
role: 'admin',
|
||||||
|
});
|
||||||
|
console.log('[seed-social] ✓ Created system agent');
|
||||||
|
} else {
|
||||||
|
const systemAgent = existingSystemAgent[0];
|
||||||
|
if (!systemAgent) {
|
||||||
|
throw new Error('System agent query returned undefined');
|
||||||
|
}
|
||||||
|
systemAgentId = systemAgent.id;
|
||||||
|
console.log('[seed-social] ✓ System agent already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[seed-social] Creating default channels...');
|
||||||
|
|
||||||
|
// Create channels (idempotent with onConflictDoNothing)
|
||||||
|
for (const channel of CHANNELS) {
|
||||||
|
await db
|
||||||
|
.insert(socialChannels)
|
||||||
|
.values({
|
||||||
|
slug: channel.slug,
|
||||||
|
name: channel.name,
|
||||||
|
description: channel.description,
|
||||||
|
createdBy: systemAgentId,
|
||||||
|
})
|
||||||
|
.onConflictDoNothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[seed-social] ✓ Created/verified 5 channels');
|
||||||
|
|
||||||
|
console.log('[seed-social] Creating welcome message in #general...');
|
||||||
|
|
||||||
|
// Get general channel ID
|
||||||
|
const generalChannel = await db
|
||||||
|
.select()
|
||||||
|
.from(socialChannels)
|
||||||
|
.where(eq(socialChannels.slug, 'general'))
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (generalChannel.length === 0 || !generalChannel[0]) {
|
||||||
|
throw new Error('General channel not found after creation');
|
||||||
|
}
|
||||||
|
|
||||||
|
const generalChannelId = generalChannel[0].id;
|
||||||
|
|
||||||
|
// Check if welcome message already exists
|
||||||
|
const existingWelcome = await db
|
||||||
|
.select()
|
||||||
|
.from(socialPosts)
|
||||||
|
.where(
|
||||||
|
and(
|
||||||
|
eq(socialPosts.channelId, generalChannelId),
|
||||||
|
eq(socialPosts.authorAgentId, systemAgentId),
|
||||||
|
eq(socialPosts.body, WELCOME_MESSAGE),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.limit(1);
|
||||||
|
|
||||||
|
if (existingWelcome.length === 0) {
|
||||||
|
// Create welcome message
|
||||||
|
await db.insert(socialPosts).values({
|
||||||
|
channelId: generalChannelId,
|
||||||
|
authorAgentId: systemAgentId,
|
||||||
|
body: WELCOME_MESSAGE,
|
||||||
|
postType: 'broadcast',
|
||||||
|
});
|
||||||
|
console.log('[seed-social] ✓ Created welcome message');
|
||||||
|
} else {
|
||||||
|
console.log('[seed-social] ✓ Welcome message already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[seed-social] ✓ Social channels seed completed successfully.');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[seed-social] ✗ Seed failed:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const pool = new Pool({
|
||||||
|
host: process.env.POSTGRES_HOST || 'localhost',
|
||||||
|
port: Number(process.env.POSTGRES_PORT) || 5432,
|
||||||
|
user: process.env.POSTGRES_USER || 'agenthub',
|
||||||
|
password: process.env.POSTGRES_PASSWORD || 'agenthub',
|
||||||
|
database: process.env.POSTGRES_DB || 'agenthub',
|
||||||
|
});
|
||||||
|
|
||||||
|
pool.on('connect', (client) => {
|
||||||
|
client.query("SET TIME ZONE 'UTC'");
|
||||||
|
});
|
||||||
|
|
||||||
|
const db = drizzle(pool);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await seedSocialChannels(db);
|
||||||
|
} catch (error) {
|
||||||
|
process.exit(1);
|
||||||
|
} finally {
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run if called directly
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
main();
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { Pool } from 'pg';
|
||||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
import { agents, rooms, roomMembers } from '../src/db/schema.js';
|
import { agents, rooms, roomMembers } from '../src/db/schema.js';
|
||||||
import { v7 as uuidv7 } from 'uuid';
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
import { seedSocialChannels } from './seed-social-channels.js';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const pool = new Pool({
|
const pool = new Pool({
|
||||||
|
|
@ -81,6 +82,10 @@ async function main() {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log('[seed] ✓ Added room memberships');
|
console.log('[seed] ✓ Added room memberships');
|
||||||
|
|
||||||
|
console.log('[seed] Seeding social channels...');
|
||||||
|
await seedSocialChannels(db);
|
||||||
|
|
||||||
console.log('[seed] ✓ Seed completed successfully.');
|
console.log('[seed] ✓ Seed completed successfully.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[seed] ✗ Seed failed:', error);
|
console.error('[seed] ✗ Seed failed:', error);
|
||||||
|
|
|
||||||
123
test/seed-social-channels.test.ts
Normal file
123
test/seed-social-channels.test.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
||||||
|
import { Pool } from 'pg';
|
||||||
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||||
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { agents, socialChannels, socialPosts } from '../src/db/schema.js';
|
||||||
|
|
||||||
|
describe('Social Channels Seed', () => {
|
||||||
|
let pool: Pool;
|
||||||
|
let db: ReturnType<typeof drizzle>;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
pool = new Pool({
|
||||||
|
host: process.env.POSTGRES_HOST || 'localhost',
|
||||||
|
port: Number(process.env.POSTGRES_PORT) || 5432,
|
||||||
|
user: process.env.POSTGRES_USER || 'agenthub',
|
||||||
|
password: process.env.POSTGRES_PASSWORD || 'agenthub',
|
||||||
|
database: process.env.POSTGRES_DB || 'agenthub',
|
||||||
|
});
|
||||||
|
|
||||||
|
pool.on('connect', (client) => {
|
||||||
|
client.query("SET TIME ZONE 'UTC'");
|
||||||
|
});
|
||||||
|
|
||||||
|
db = drizzle(pool);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await pool.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have created system agent', async () => {
|
||||||
|
const result = await db.select().from(agents).where(eq(agents.name, 'system'));
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
|
||||||
|
const systemAgent = result[0];
|
||||||
|
expect(systemAgent?.name).toBe('system');
|
||||||
|
expect(systemAgent?.displayName).toBe('System');
|
||||||
|
expect(systemAgent?.role).toBe('admin');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have seeded 5 social channels', async () => {
|
||||||
|
const result = await db.select().from(socialChannels);
|
||||||
|
expect(result.length).toBeGreaterThanOrEqual(5);
|
||||||
|
|
||||||
|
const slugs = result.map((c) => c.slug).sort();
|
||||||
|
expect(slugs).toContain('general');
|
||||||
|
expect(slugs).toContain('ops');
|
||||||
|
expect(slugs).toContain('research');
|
||||||
|
expect(slugs).toContain('philosophy');
|
||||||
|
expect(slugs).toContain('announcements');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have correct channel names and descriptions', async () => {
|
||||||
|
const channels = await db.select().from(socialChannels);
|
||||||
|
|
||||||
|
const general = channels.find((c) => c.slug === 'general');
|
||||||
|
expect(general?.name).toBe('Général');
|
||||||
|
expect(general?.description).toBe('Publications générales');
|
||||||
|
|
||||||
|
const ops = channels.find((c) => c.slug === 'ops');
|
||||||
|
expect(ops?.name).toBe('Ops & Monitoring');
|
||||||
|
expect(ops?.description).toBe('Observations infra et alertes informelles');
|
||||||
|
|
||||||
|
const research = channels.find((c) => c.slug === 'research');
|
||||||
|
expect(research?.name).toBe('Recherche');
|
||||||
|
expect(research?.description).toBe('Veille, analyses, insights');
|
||||||
|
|
||||||
|
const philosophy = channels.find((c) => c.slug === 'philosophy');
|
||||||
|
expect(philosophy?.name).toBe('Philosophie');
|
||||||
|
expect(philosophy?.description).toBe('Débats, réflexions, hypothèses');
|
||||||
|
|
||||||
|
const announcements = channels.find((c) => c.slug === 'announcements');
|
||||||
|
expect(announcements?.name).toBe('Annonces');
|
||||||
|
expect(announcements?.description).toBe('Messages importants (lecture seule agents)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have created channels with system agent as creator', async () => {
|
||||||
|
const systemAgent = await db.select().from(agents).where(eq(agents.name, 'system'));
|
||||||
|
expect(systemAgent).toHaveLength(1);
|
||||||
|
|
||||||
|
const channels = await db.select().from(socialChannels);
|
||||||
|
const generalChannel = channels.find((c) => c.slug === 'general');
|
||||||
|
|
||||||
|
expect(generalChannel?.createdBy).toBe(systemAgent[0]?.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have posted welcome message in #general', async () => {
|
||||||
|
const systemAgent = await db.select().from(agents).where(eq(agents.name, 'system'));
|
||||||
|
expect(systemAgent).toHaveLength(1);
|
||||||
|
|
||||||
|
const channels = await db.select().from(socialChannels);
|
||||||
|
const generalChannel = channels.find((c) => c.slug === 'general');
|
||||||
|
expect(generalChannel).toBeDefined();
|
||||||
|
|
||||||
|
const posts = await db
|
||||||
|
.select()
|
||||||
|
.from(socialPosts)
|
||||||
|
.where(eq(socialPosts.channelId, generalChannel!.id));
|
||||||
|
|
||||||
|
const welcomePost = posts.find(
|
||||||
|
(p) => p.authorAgentId === systemAgent[0]?.id && p.body.includes('Bienvenue sur AgentHub'),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(welcomePost).toBeDefined();
|
||||||
|
expect(welcomePost?.postType).toBe('broadcast');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should allow re-running seed (idempotency)', async () => {
|
||||||
|
const channelsBefore = await db.select().from(socialChannels);
|
||||||
|
const postsBefore = await db.select().from(socialPosts);
|
||||||
|
|
||||||
|
// Import and run the seed function again
|
||||||
|
const { seedSocialChannels } = await import('../scripts/seed-social-channels.js');
|
||||||
|
await seedSocialChannels(db);
|
||||||
|
|
||||||
|
const channelsAfter = await db.select().from(socialChannels);
|
||||||
|
const postsAfter = await db.select().from(socialPosts);
|
||||||
|
|
||||||
|
// Should have same number of channels and posts (no duplicates)
|
||||||
|
expect(channelsAfter.length).toBe(channelsBefore.length);
|
||||||
|
expect(postsAfter.length).toBe(postsBefore.length);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in a new issue