Skip to content

Commit 2339aa7

Browse files
authored
Merge pull request #781 from zackproser/improve-comparisons-again
Add production seeding script and update gitignore
2 parents b7b7526 + dcbee73 commit 2339aa7

File tree

14 files changed

+415
-453
lines changed

14 files changed

+415
-453
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,4 @@ prisma/.env
6767

6868
# Ignore optimized OG images
6969
public/og-images-optimized/
70+
.env.production

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@
2727
"og:clean-orphans": "tsx --tsconfig scripts/tsconfig.json scripts/clean-og-images.ts",
2828
"og:optimize": "node scripts/optimize-og-images.js",
2929
"og:optimize-replace": "node scripts/optimize-og-images.js --replace",
30-
"prisma:seed": "tsx --tsconfig prisma/tsconfig.json prisma/seed.ts"
30+
"prisma:seed": "tsx --tsconfig prisma/tsconfig.json prisma/seed.ts",
31+
"prisma:seed-production": "./scripts/seed-production.sh"
3132
},
3233
"browserslist": "defaults, not ie <= 11",
3334
"dependencies": {

scripts/check-production-tools.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { PrismaClient } = require('@prisma/client');
2+
3+
async function checkProductionTools() {
4+
console.log('🔍 Checking production database tools...\n');
5+
6+
const prisma = new PrismaClient();
7+
8+
try {
9+
const tools = await prisma.tool.findMany({
10+
select: {
11+
name: true,
12+
category: true,
13+
websiteUrl: true
14+
},
15+
orderBy: {
16+
name: 'asc'
17+
}
18+
});
19+
20+
console.log(`📊 Found ${tools.length} tools in production database:\n`);
21+
22+
tools.forEach((tool, index) => {
23+
console.log(`${index + 1}. ${tool.name} (${tool.category})`);
24+
});
25+
26+
console.log('\n🔗 Testing slug generation for problematic URLs:');
27+
28+
const problematicSlugs = [
29+
'anthropic-claude-api',
30+
'mistral-ai',
31+
'cursor',
32+
'github-copilot'
33+
];
34+
35+
for (const slug of problematicSlugs) {
36+
// Generate search patterns (same logic as in getToolBySlug)
37+
const patterns = [
38+
slug,
39+
slug.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' ')
40+
];
41+
42+
const tool = await prisma.tool.findFirst({
43+
where: {
44+
OR: patterns.flatMap(pattern => [
45+
{ name: { equals: pattern, mode: 'insensitive' } },
46+
{ name: { contains: pattern, mode: 'insensitive' } }
47+
])
48+
}
49+
});
50+
51+
console.log(` ${slug}: ${tool ? '✅ ' + tool.name : '❌ Not found'}`);
52+
}
53+
54+
} catch (error) {
55+
console.error('❌ Error checking production tools:', error.message);
56+
} finally {
57+
await prisma.$disconnect();
58+
}
59+
}
60+
61+
checkProductionTools();

scripts/seed-production.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/bash
2+
3+
# This script runs the prisma seed against the production database
4+
# Usage: ./scripts/seed-production.sh
5+
6+
echo "🌱 Production Database Seeding Script"
7+
echo "======================================"
8+
9+
# Check if we have the required environment file
10+
if [ ! -f ".env.production" ]; then
11+
echo "❌ Error: .env.production file not found"
12+
echo "Please create .env.production with your production database URLs"
13+
exit 1
14+
fi
15+
16+
# Get the production database URL (non-pooling for direct operations)
17+
PROD_DB_URL=$(grep POSTGRES_URL_NON_POOLING .env.production | cut -d '=' -f2- | tr -d '"')
18+
19+
if [ -z "$PROD_DB_URL" ]; then
20+
echo "❌ Error: Could not find POSTGRES_URL_NON_POOLING in .env.production"
21+
echo "Please make sure you have POSTGRES_URL_NON_POOLING set in .env.production"
22+
exit 1
23+
fi
24+
25+
echo "🔍 Found production database URL"
26+
echo "⚠️ WARNING: This will modify your PRODUCTION database!"
27+
echo "⚠️ This will DELETE all existing tools and replace them with the seed data"
28+
echo ""
29+
echo "Current seed data contains 25 tools:"
30+
echo "- Anthropic Claude API, Mistral AI, Cursor, GitHub Copilot, etc."
31+
echo ""
32+
echo "Continue? (y/n)"
33+
read -r confirm
34+
35+
if [ "$confirm" != "y" ]; then
36+
echo "❌ Operation cancelled"
37+
exit 0
38+
fi
39+
40+
echo ""
41+
echo "🚀 Running seed script against production database..."
42+
echo "📊 This will:"
43+
echo " 1. Delete all existing tools"
44+
echo " 2. Insert 25 new tools from seed data"
45+
echo " 3. Update comparison URLs to work correctly"
46+
echo ""
47+
48+
# Run the seed script with the production database URL
49+
POSTGRES_URL="$PROD_DB_URL" npm run prisma:seed
50+
51+
if [ $? -eq 0 ]; then
52+
echo ""
53+
echo "✅ Production database seeding completed successfully!"
54+
echo "🎉 Your comparison URLs should now work correctly"
55+
echo ""
56+
echo "Next steps:"
57+
echo "1. Test a few comparison URLs to verify they work"
58+
echo "2. Check that the tools are displaying correctly on your site"
59+
echo "3. Monitor for any issues in production"
60+
else
61+
echo ""
62+
echo "❌ Seeding failed! Please check the error messages above"
63+
echo "Your production database was not modified"
64+
exit 1
65+
fi

src/app/api/articles/[slug]/__tests__/route.test.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,18 @@
33
import { NextRequest, NextResponse } from 'next/server';
44
import { jest } from '@jest/globals';
55

6-
// Mock the importContentMetadata function
6+
// Mock the content-handlers functions
77
jest.mock('@/lib/content-handlers', () => ({
8-
importContentMetadata: jest.fn()
8+
getContentItemByDirectorySlug: jest.fn()
99
}));
1010

1111
// Import the mocked function
12-
import { importContentMetadata } from '@/lib/content-handlers';
13-
14-
// Mock the route handlers directly
15-
const mockGET = jest.fn();
12+
import { getContentItemByDirectorySlug } from '@/lib/content-handlers';
1613

1714
// Mock the route module
15+
const mockGET = jest.fn() as jest.MockedFunction<any>;
1816
jest.mock('../route', () => ({
19-
GET: (...args: any[]) => mockGET(...args)
17+
GET: mockGET
2018
}));
2119

2220
describe('Articles API', () => {
@@ -37,10 +35,8 @@ describe('Articles API', () => {
3735
tags: ['test', 'article']
3836
};
3937

40-
// Setup mock to return article data
41-
mockGET.mockResolvedValue(
42-
NextResponse.json(mockArticle, { status: 200 })
43-
);
38+
const mockResponse = NextResponse.json(mockArticle, { status: 200 });
39+
mockGET.mockResolvedValue(mockResponse);
4440

4541
const request = new NextRequest('http://localhost:3000/api/articles/test-article');
4642
const params = { params: Promise.resolve({ slug: 'test-article' }) };
@@ -55,10 +51,8 @@ describe('Articles API', () => {
5551
});
5652

5753
it('should return 404 for non-existent article', async () => {
58-
// Setup mock to simulate article not found
59-
mockGET.mockResolvedValue(
60-
NextResponse.json({ error: 'Article not found' }, { status: 404 })
61-
);
54+
const mockResponse = NextResponse.json({ error: 'Article not found' }, { status: 404 });
55+
mockGET.mockResolvedValue(mockResponse);
6256

6357
const request = new NextRequest('http://localhost:3000/api/articles/non-existent');
6458
const params = { params: Promise.resolve({ slug: 'non-existent' }) };
@@ -73,13 +67,8 @@ describe('Articles API', () => {
7367
});
7468

7569
it('should handle errors gracefully', async () => {
76-
// Setup mock to throw an error
77-
mockGET.mockRejectedValue(new Error('Unexpected error'));
78-
79-
// Setup error handler mock
80-
mockGET.mockResolvedValueOnce(
81-
NextResponse.json({ error: 'Internal server error' }, { status: 500 })
82-
);
70+
const mockResponse = NextResponse.json({ error: 'Internal server error' }, { status: 500 });
71+
mockGET.mockResolvedValue(mockResponse);
8372

8473
const request = new NextRequest('http://localhost:3000/api/articles/error-article');
8574
const params = { params: Promise.resolve({ slug: 'error-article' }) };

src/app/api/articles/__tests__/route.test.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { NextRequest, NextResponse } from 'next/server';
44
import { jest } from '@jest/globals';
55

66
// Mock the route handlers directly
7-
const mockGET = jest.fn();
7+
const mockGET = jest.fn() as jest.MockedFunction<any>;
88

99
// Mock the route module
1010
jest.mock('../[slug]/route', () => ({
11-
GET: (...args) => mockGET(...args)
11+
GET: mockGET
1212
}));
1313

1414
describe('Articles API', () => {
@@ -30,9 +30,8 @@ describe('Articles API', () => {
3030
};
3131

3232
// Setup mock to return article data
33-
mockGET.mockResolvedValue(
34-
NextResponse.json(mockArticle, { status: 200 })
35-
);
33+
const mockResponse = NextResponse.json(mockArticle, { status: 200 });
34+
mockGET.mockResolvedValue(mockResponse);
3635

3736
const request = new NextRequest('http://localhost:3000/api/articles/test-article');
3837
const params = { params: Promise.resolve({ slug: 'test-article' }) };
@@ -48,9 +47,8 @@ describe('Articles API', () => {
4847

4948
it('should return 404 for non-existent article', async () => {
5049
// Setup mock to simulate article not found
51-
mockGET.mockResolvedValue(
52-
NextResponse.json({ error: 'Article not found' }, { status: 404 })
53-
);
50+
const mockResponse = NextResponse.json({ error: 'Article not found' }, { status: 404 });
51+
mockGET.mockResolvedValue(mockResponse);
5452

5553
const request = new NextRequest('http://localhost:3000/api/articles/non-existent');
5654
const params = { params: Promise.resolve({ slug: 'non-existent' }) };

0 commit comments

Comments
 (0)