fix bruno collection

This commit is contained in:
Kazuma
2026-06-06 21:49:31 -04:00
parent 2e7860637e
commit 76c2a671f4
25 changed files with 4361 additions and 94 deletions
+270
View File
@@ -0,0 +1,270 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { EtfScorer } from '../server/domains/screener/scorers/EtfScorer.js';
import { ScoringConfig } from '../server/domains/shared/scoring/ScoringConfig.js';
import type { EtfMetrics } from '../server/domains/shared/types/models.model.js';
const DEFAULT_RULES = {
gates: ScoringConfig.base.gates.ETF,
weights: ScoringConfig.base.weights.ETF,
thresholds: ScoringConfig.base.thresholds.ETF,
};
test('EtfScorer', async (t) => {
await t.test('rejects ETF with high expense ratio', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.8, // Exceeds 0.2% gate
yield: 2.5,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
assert.ok(result.scoreSummary.includes('Expense ratio'));
});
await t.test('accepts ETF with low expense ratio', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.05, // Well below 0.2% gate
yield: 2.5,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.notEqual(result.label, '🔴 REJECT');
});
await t.test('rejects ETF with zero expense ratio (data issue)', () => {
const metrics: EtfMetrics = {
expenseRatio: 0, // Zero treated as missing/invalid
yield: 2.5,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
// Zero is treated as null/missing data
assert.ok(result);
});
await t.test('rejects ETF with low 5-year return', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 5000000,
fiveYearReturn: 5, // Below 8% gate
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
assert.ok(result.scoreSummary.includes('5-year return'));
});
await t.test('accepts ETF with strong 5-year return', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 5000000,
fiveYearReturn: 12, // Above 8% gate
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.notEqual(result.label, '🔴 REJECT');
});
await t.test('rejects ETF with insufficient volume', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 500000, // Below 1M gate
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
assert.ok(result.scoreSummary.includes('Volume'));
});
await t.test('accepts ETF with strong volume', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 10000000, // Well above 1M gate
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.notEqual(result.label, '🔴 REJECT');
});
await t.test('scores high-quality ETF positively', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.05, // Low
yield: 3.0, // Decent
volume: 20000000, // High
fiveYearReturn: 15, // Strong
totalAssets: 50e9, // Large
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.notEqual(result.label, '🔴 REJECT');
assert.ok(result.audit?.passedGates);
});
await t.test('handles null/undefined metrics gracefully', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: null,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: null,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
// Should not crash, null values are skipped
assert.ok(result);
});
await t.test('includes audit trail of gate checks', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.8,
yield: 2.5,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.ok(result.audit);
if (!result.audit?.passedGates) {
assert.ok(Array.isArray(result.audit?.failures));
}
});
await t.test('scores yield as secondary factor', () => {
const metricsLowYield: EtfMetrics = {
expenseRatio: 0.1,
yield: 0.5, // Low yield
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const metricsHighYield: EtfMetrics = {
expenseRatio: 0.1,
yield: 4.0, // High yield
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const resultLow = EtfScorer.score(metricsLowYield, DEFAULT_RULES);
const resultHigh = EtfScorer.score(metricsHighYield, DEFAULT_RULES);
// Both should pass gates
assert.ok(resultLow.audit?.passedGates);
assert.ok(resultHigh.audit?.passedGates);
});
await t.test('rejects ETF with multiple gate failures', () => {
const metrics: EtfMetrics = {
expenseRatio: 1.0, // High
yield: 0.5,
volume: 100000, // Low
fiveYearReturn: 3, // Low
totalAssets: 100e6, // Small
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
// Should have multiple failures
if (!result.audit?.passedGates && result.audit?.failures) {
assert.ok(result.audit.failures.length > 1);
}
});
await t.test('calculates score based on thresholds', () => {
const metricsLowReturn: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 5000000,
fiveYearReturn: 8.5, // Just above gate
totalAssets: 5e9,
};
const metricsHighReturn: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 5000000,
fiveYearReturn: 15, // Well above gate
totalAssets: 5e9,
};
const resultLow = EtfScorer.score(metricsLowReturn, DEFAULT_RULES);
const resultHigh = EtfScorer.score(metricsHighReturn, DEFAULT_RULES);
// Both should pass
assert.ok(resultLow.audit?.passedGates);
assert.ok(resultHigh.audit?.passedGates);
});
await t.test('penalizes low-volume ETF', () => {
const metricsLowVolume: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 2000000, // Low but above gate
fiveYearReturn: 10,
totalAssets: 5e9,
};
const metricsHighVolume: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 50000000, // High volume
fiveYearReturn: 10,
totalAssets: 5e9,
};
const resultLow = EtfScorer.score(metricsLowVolume, DEFAULT_RULES);
const resultHigh = EtfScorer.score(metricsHighVolume, DEFAULT_RULES);
// Both pass gates, but high volume should score higher
assert.ok(resultLow.audit?.passedGates);
assert.ok(resultHigh.audit?.passedGates);
});
await t.test('handles extremely high expense ratio', () => {
const metrics: EtfMetrics = {
expenseRatio: 5.0, // 5%!
yield: 2.5,
volume: 5000000,
fiveYearReturn: 10,
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
});
await t.test('handles negative 5-year return', () => {
const metrics: EtfMetrics = {
expenseRatio: 0.1,
yield: 2.5,
volume: 5000000,
fiveYearReturn: -5, // Negative return
totalAssets: 5e9,
};
const result = EtfScorer.score(metrics, DEFAULT_RULES);
assert.equal(result.label, '🔴 REJECT');
});
});