openstock/__tests__/adanos.actions.test.ts

213 lines
6.8 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from 'vitest';
import {
getStockSentimentInsights,
} from '@/lib/actions/adanos.actions';
import {
buildStockSentimentInsights,
getSourceAlignment,
normalizeSourceInsight,
} from '@/lib/actions/adanos.helpers';
afterEach(() => {
vi.restoreAllMocks();
delete process.env.ADANOS_API_KEY;
delete process.env.ADANOS_API_BASE_URL;
});
describe('normalizeSourceInsight', () => {
it('maps source-specific metrics for mentions and trades', () => {
const reddit = normalizeSourceInsight('reddit', {
ticker: 'TSLA',
buzz_score: 81.2,
bullish_pct: 46,
trend: 'rising',
mentions: 647,
});
const polymarket = normalizeSourceInsight('polymarket', {
ticker: 'TSLA',
buzz_score: 55.7,
bullish_pct: 72,
trend: 'stable',
trade_count: 3731,
});
expect(reddit).toMatchObject({
label: 'Reddit',
companyName: null,
metricLabel: 'Mentions',
metricValue: 647,
buzzScore: 81.2,
bullishPct: 46,
});
expect(polymarket).toMatchObject({
label: 'Polymarket',
companyName: null,
metricLabel: 'Trades',
metricValue: 3731,
buzzScore: 55.7,
bullishPct: 72,
});
});
it('returns null when required values are missing', () => {
expect(
normalizeSourceInsight('x', {
ticker: 'NVDA',
bullish_pct: 54,
mentions: 1200,
}),
).toBeNull();
expect(
normalizeSourceInsight('news', {
ticker: 'NVDA',
buzz_score: 60,
bullish_pct: 54,
}),
).toBeNull();
});
});
describe('getSourceAlignment', () => {
it('classifies wide divergence when sources materially disagree', () => {
expect(getSourceAlignment([31, 56, 48, 30])).toBe('Wide divergence');
});
it('classifies bullish alignment when sources are tightly aligned and positive', () => {
expect(getSourceAlignment([61, 64, 67])).toBe('Bullish alignment');
});
});
describe('buildStockSentimentInsights', () => {
it('builds a compact aggregate summary from available sources', () => {
const insight = buildStockSentimentInsights('TSLA', [
{
source: 'reddit',
label: 'Reddit',
companyName: 'Tesla, Inc.',
buzzScore: 74.1,
bullishPct: 31,
trend: 'rising',
metricLabel: 'Mentions',
metricValue: 647,
},
{
source: 'x',
label: 'X.com',
companyName: 'Tesla, Inc.',
buzzScore: 86.1,
bullishPct: 56,
trend: 'falling',
metricLabel: 'Mentions',
metricValue: 2650,
},
{
source: 'polymarket',
label: 'Polymarket',
companyName: 'Tesla, Inc.',
buzzScore: 83.3,
bullishPct: 30,
trend: 'falling',
metricLabel: 'Trades',
metricValue: 3731,
},
null,
]);
expect(insight).toMatchObject({
symbol: 'TSLA',
companyName: 'Tesla, Inc.',
averageBuzz: 81.2,
bullishAverage: 39,
sourceAlignment: 'Wide divergence',
availableSources: 3,
});
expect(insight?.sources).toHaveLength(3);
});
it('returns null when no sources have usable data', () => {
expect(buildStockSentimentInsights('MSFT', [null, null])).toBeNull();
});
});
describe('getStockSentimentInsights', () => {
it('returns a parsed result when compare data matches the requested ticker', async () => {
process.env.ADANOS_API_KEY = 'test-key';
vi.spyOn(global, 'fetch').mockImplementation(async (input) => {
const url = String(input);
if (url.includes('/reddit/')) {
return new Response(
JSON.stringify({
stocks: [{ ticker: 'TSLA', company_name: 'Tesla, Inc.', buzz_score: 80, bullish_pct: 40, trend: 'rising', mentions: 10 }],
}),
{ status: 200 },
);
}
if (url.includes('/x/')) {
return new Response(
JSON.stringify({
stocks: [{ ticker: 'TSLA', company_name: 'Tesla, Inc.', buzz_score: 90, bullish_pct: 60, trend: 'falling', mentions: 20 }],
}),
{ status: 200 },
);
}
return new Response(JSON.stringify({ stocks: [] }), { status: 404 });
});
const insight = await getStockSentimentInsights('TSLA');
expect(insight).toMatchObject({
symbol: 'TSLA',
companyName: 'Tesla, Inc.',
averageBuzz: 85,
bullishAverage: 50,
availableSources: 2,
});
expect(insight?.sources).toHaveLength(2);
});
it('returns null when the remote source returns 404 for all sources', async () => {
process.env.ADANOS_API_KEY = 'test-key';
vi.spyOn(global, 'fetch').mockResolvedValue(new Response(null, { status: 404 }));
await expect(getStockSentimentInsights('TSLA')).resolves.toBeNull();
});
it('returns null when the remote payload contains a different ticker only', async () => {
process.env.ADANOS_API_KEY = 'test-key';
vi.spyOn(global, 'fetch').mockResolvedValue(
new Response(
JSON.stringify({
stocks: [{ ticker: 'MSFT', company_name: 'Microsoft Corporation', buzz_score: 70, bullish_pct: 55, trend: 'stable', mentions: 30 }],
}),
{ status: 200 },
),
);
await expect(getStockSentimentInsights('TSLA')).resolves.toBeNull();
});
it('returns null when the response body is invalid json', async () => {
process.env.ADANOS_API_KEY = 'test-key';
vi.spyOn(global, 'fetch').mockResolvedValue({
status: 200,
ok: true,
json: vi.fn().mockRejectedValue(new Error('invalid json')),
} as unknown as Response);
await expect(getStockSentimentInsights('TSLA')).resolves.toBeNull();
});
it('returns null when fetch fails', async () => {
process.env.ADANOS_API_KEY = 'test-key';
vi.spyOn(global, 'fetch').mockRejectedValue(new Error('network failed'));
await expect(getStockSentimentInsights('TSLA')).resolves.toBeNull();
});
});