bulltrackers-module 1.0.766 → 1.0.768
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/functions/computation-system-v2/computations/BehavioralAnomaly.js +298 -186
- package/functions/computation-system-v2/computations/NewSectorExposure.js +82 -35
- package/functions/computation-system-v2/computations/NewSocialPost.js +52 -24
- package/functions/computation-system-v2/computations/PopularInvestorProfileMetrics.js +354 -641
- package/functions/computation-system-v2/config/bulltrackers.config.js +26 -14
- package/functions/computation-system-v2/framework/core/Manifest.js +9 -16
- package/functions/computation-system-v2/framework/core/RunAnalyzer.js +2 -1
- package/functions/computation-system-v2/framework/data/DataFetcher.js +142 -4
- package/functions/computation-system-v2/framework/execution/Orchestrator.js +18 -31
- package/functions/computation-system-v2/framework/storage/StorageManager.js +7 -17
- package/functions/computation-system-v2/framework/testing/ComputationTester.js +155 -66
- package/functions/computation-system-v2/scripts/test-computation-dag.js +109 -0
- package/functions/task-engine/helpers/data_storage_helpers.js +6 -6
- package/package.json +1 -1
- package/functions/computation-system-v2/computations/PopularInvestorRiskAssessment.js +0 -176
- package/functions/computation-system-v2/computations/PopularInvestorRiskMetrics.js +0 -294
- package/functions/computation-system-v2/computations/UserPortfolioSummary.js +0 -172
- package/functions/computation-system-v2/scripts/migrate-sectors.js +0 -73
- package/functions/computation-system-v2/test/analyze-results.js +0 -238
- package/functions/computation-system-v2/test/other/test-dependency-cascade.js +0 -150
- package/functions/computation-system-v2/test/other/test-dispatcher.js +0 -317
- package/functions/computation-system-v2/test/other/test-framework.js +0 -500
- package/functions/computation-system-v2/test/other/test-real-execution.js +0 -166
- package/functions/computation-system-v2/test/other/test-real-integration.js +0 -194
- package/functions/computation-system-v2/test/other/test-refactor-e2e.js +0 -131
- package/functions/computation-system-v2/test/other/test-results.json +0 -31
- package/functions/computation-system-v2/test/other/test-risk-metrics-computation.js +0 -329
- package/functions/computation-system-v2/test/other/test-scheduler.js +0 -204
- package/functions/computation-system-v2/test/other/test-storage.js +0 -449
- package/functions/computation-system-v2/test/run-pipeline-test.js +0 -554
- package/functions/computation-system-v2/test/test-full-pipeline.js +0 -227
- package/functions/computation-system-v2/test/test-worker-pool.js +0 -266
|
@@ -1,238 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Test Results Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes test reports and compares them across runs.
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* node test/analyze-results.js --report test-report-123.json
|
|
8
|
-
* node test/analyze-results.js --compare report1.json report2.json
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
|
|
14
|
-
class TestResultsAnalyzer {
|
|
15
|
-
constructor(reportPath) {
|
|
16
|
-
this.report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
analyze() {
|
|
20
|
-
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
21
|
-
console.log('║ TEST RESULTS DETAILED ANALYSIS ║');
|
|
22
|
-
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
23
|
-
|
|
24
|
-
this._printOverview();
|
|
25
|
-
this._analyzeLineage();
|
|
26
|
-
this._analyzeCosts();
|
|
27
|
-
this._analyzePerformance();
|
|
28
|
-
this._checkForIssues();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
_printOverview() {
|
|
32
|
-
console.log('📋 OVERVIEW\n');
|
|
33
|
-
console.log(` Run ID: ${this.report.runId}`);
|
|
34
|
-
console.log(` Status: ${this.report.status}`);
|
|
35
|
-
console.log(` Start: ${this.report.startTime}`);
|
|
36
|
-
console.log(` End: ${this.report.endTime}`);
|
|
37
|
-
|
|
38
|
-
const duration = new Date(this.report.endTime) - new Date(this.report.startTime);
|
|
39
|
-
console.log(` Duration: ${Math.round(duration / 1000)}s\n`);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
_analyzeLineage() {
|
|
43
|
-
console.log('🔗 LINEAGE ANALYSIS\n');
|
|
44
|
-
|
|
45
|
-
const { dependencyGraph, passSummary } = this.report.lineage;
|
|
46
|
-
|
|
47
|
-
// Check for correct topological ordering
|
|
48
|
-
console.log(' Pass Distribution:');
|
|
49
|
-
for (const [pass, comps] of Object.entries(passSummary)) {
|
|
50
|
-
console.log(` Pass ${pass}: ${comps.length} computations`);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Verify dependencies executed before dependents
|
|
54
|
-
console.log('\n Dependency Verification:');
|
|
55
|
-
let violations = 0;
|
|
56
|
-
|
|
57
|
-
for (const comp of dependencyGraph) {
|
|
58
|
-
if (comp.dependencies.length === 0) continue;
|
|
59
|
-
|
|
60
|
-
for (const dep of comp.dependencies) {
|
|
61
|
-
const depData = dependencyGraph.find(c => c.computation === dep);
|
|
62
|
-
if (!depData) {
|
|
63
|
-
console.log(` ⚠️ ${comp.computation}: dependency ${dep} not found`);
|
|
64
|
-
violations++;
|
|
65
|
-
} else if (depData.pass >= comp.pass) {
|
|
66
|
-
console.log(` ❌ ${comp.computation} (Pass ${comp.pass}) runs before/same as dependency ${dep} (Pass ${depData.pass})`);
|
|
67
|
-
violations++;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (violations === 0) {
|
|
73
|
-
console.log(' ✅ All dependencies correctly ordered');
|
|
74
|
-
}
|
|
75
|
-
console.log('');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
_analyzeCosts() {
|
|
79
|
-
console.log('💰 COST ANALYSIS\n');
|
|
80
|
-
|
|
81
|
-
const { totalCostUSD, maxCostUSD, utilizationPct, byComputation } = this.report.cost;
|
|
82
|
-
|
|
83
|
-
console.log(` Total Cost: $${totalCostUSD.toFixed(6)}`);
|
|
84
|
-
console.log(` Budget: $${maxCostUSD}`);
|
|
85
|
-
console.log(` Utilization: ${utilizationPct.toFixed(1)}%`);
|
|
86
|
-
|
|
87
|
-
// Cost efficiency rating
|
|
88
|
-
let rating = '🟢 Excellent';
|
|
89
|
-
if (utilizationPct > 80) rating = '🔴 High (near limit)';
|
|
90
|
-
else if (utilizationPct > 50) rating = '🟡 Moderate';
|
|
91
|
-
console.log(` Rating: ${rating}\n`);
|
|
92
|
-
|
|
93
|
-
// Top cost drivers
|
|
94
|
-
console.log(' Top 5 Cost Drivers:');
|
|
95
|
-
byComputation.slice(0, 5).forEach((item, i) => {
|
|
96
|
-
const pct = (item.cost / totalCostUSD) * 100;
|
|
97
|
-
console.log(` ${i + 1}. ${item.name}: $${item.cost.toFixed(6)} (${pct.toFixed(1)}%)`);
|
|
98
|
-
});
|
|
99
|
-
console.log('');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
_analyzePerformance() {
|
|
103
|
-
console.log('⚡ PERFORMANCE ANALYSIS\n');
|
|
104
|
-
|
|
105
|
-
const comps = this.report.computations;
|
|
106
|
-
const durations = comps.map(c => c.duration);
|
|
107
|
-
const avgDuration = durations.reduce((a, b) => a + b, 0) / durations.length;
|
|
108
|
-
const maxDuration = Math.max(...durations);
|
|
109
|
-
const totalDuration = durations.reduce((a, b) => a + b, 0);
|
|
110
|
-
|
|
111
|
-
console.log(` Total Execution Time: ${totalDuration}ms`);
|
|
112
|
-
console.log(` Average per Computation: ${Math.round(avgDuration)}ms`);
|
|
113
|
-
console.log(` Slowest: ${maxDuration}ms\n`);
|
|
114
|
-
|
|
115
|
-
// Slowest computations
|
|
116
|
-
const slowest = [...comps]
|
|
117
|
-
.sort((a, b) => b.duration - a.duration)
|
|
118
|
-
.slice(0, 5);
|
|
119
|
-
|
|
120
|
-
console.log(' Slowest Computations:');
|
|
121
|
-
slowest.forEach((comp, i) => {
|
|
122
|
-
const pct = (comp.duration / totalDuration) * 100;
|
|
123
|
-
console.log(` ${i + 1}. ${comp.name}: ${comp.duration}ms (${pct.toFixed(1)}%)`);
|
|
124
|
-
});
|
|
125
|
-
console.log('');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
_checkForIssues() {
|
|
129
|
-
console.log('🔍 ISSUE DETECTION\n');
|
|
130
|
-
|
|
131
|
-
const issues = [];
|
|
132
|
-
|
|
133
|
-
// Check cost utilization
|
|
134
|
-
if (this.report.cost.utilizationPct > 90) {
|
|
135
|
-
issues.push({
|
|
136
|
-
severity: 'HIGH',
|
|
137
|
-
type: 'Cost',
|
|
138
|
-
message: `Cost utilization at ${this.report.cost.utilizationPct.toFixed(1)}% - near limit`
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Check for failed computations
|
|
143
|
-
const failed = this.report.computations.filter(c => c.status === 'failed');
|
|
144
|
-
if (failed.length > 0) {
|
|
145
|
-
issues.push({
|
|
146
|
-
severity: 'HIGH',
|
|
147
|
-
type: 'Execution',
|
|
148
|
-
message: `${failed.length} computations failed`
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Check for very slow computations (> 60s)
|
|
153
|
-
const verySlow = this.report.computations.filter(c => c.duration > 60000);
|
|
154
|
-
if (verySlow.length > 0) {
|
|
155
|
-
issues.push({
|
|
156
|
-
severity: 'MEDIUM',
|
|
157
|
-
type: 'Performance',
|
|
158
|
-
message: `${verySlow.length} computations took >60s`
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (issues.length === 0) {
|
|
163
|
-
console.log(' ✅ No issues detected\n');
|
|
164
|
-
} else {
|
|
165
|
-
for (const issue of issues) {
|
|
166
|
-
const icon = issue.severity === 'HIGH' ? '🔴' : '🟡';
|
|
167
|
-
console.log(` ${icon} [${issue.severity}] ${issue.type}: ${issue.message}`);
|
|
168
|
-
}
|
|
169
|
-
console.log('');
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
compare(otherReport) {
|
|
174
|
-
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
175
|
-
console.log('║ COMPARISON ANALYSIS ║');
|
|
176
|
-
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
|
177
|
-
|
|
178
|
-
const other = JSON.parse(fs.readFileSync(otherReport, 'utf8'));
|
|
179
|
-
|
|
180
|
-
// Cost comparison
|
|
181
|
-
console.log('💰 Cost Comparison:\n');
|
|
182
|
-
const costDiff = this.report.cost.totalCostUSD - other.cost.totalCostUSD;
|
|
183
|
-
const costDiffPct = (costDiff / other.cost.totalCostUSD) * 100;
|
|
184
|
-
|
|
185
|
-
console.log(` This run: $${this.report.cost.totalCostUSD.toFixed(6)}`);
|
|
186
|
-
console.log(` Previous run: $${other.cost.totalCostUSD.toFixed(6)}`);
|
|
187
|
-
console.log(` Difference: $${Math.abs(costDiff).toFixed(6)} (${costDiffPct > 0 ? '+' : ''}${costDiffPct.toFixed(1)}%)\n`);
|
|
188
|
-
|
|
189
|
-
// Performance comparison
|
|
190
|
-
console.log('⚡ Performance Comparison:\n');
|
|
191
|
-
|
|
192
|
-
const thisDuration = new Date(this.report.endTime) - new Date(this.report.startTime);
|
|
193
|
-
const otherDuration = new Date(other.endTime) - new Date(other.startTime);
|
|
194
|
-
const durationDiff = thisDuration - otherDuration;
|
|
195
|
-
const durationDiffPct = (durationDiff / otherDuration) * 100;
|
|
196
|
-
|
|
197
|
-
console.log(` This run: ${Math.round(thisDuration / 1000)}s`);
|
|
198
|
-
console.log(` Previous run: ${Math.round(otherDuration / 1000)}s`);
|
|
199
|
-
console.log(` Difference: ${Math.abs(Math.round(durationDiff / 1000))}s (${durationDiffPct > 0 ? '+' : ''}${durationDiffPct.toFixed(1)}%)\n`);
|
|
200
|
-
|
|
201
|
-
// Entity count comparison
|
|
202
|
-
console.log('📊 Entity Count Comparison:\n');
|
|
203
|
-
console.log(` This run: ${this.report.storage.totalEntities}`);
|
|
204
|
-
console.log(` Previous run: ${other.storage.totalEntities}\n`);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ============================================================================
|
|
209
|
-
// CLI
|
|
210
|
-
// ============================================================================
|
|
211
|
-
|
|
212
|
-
function main() {
|
|
213
|
-
const args = process.argv.slice(2);
|
|
214
|
-
|
|
215
|
-
if (args.length === 0) {
|
|
216
|
-
console.log('Usage:');
|
|
217
|
-
console.log(' node test/analyze-results.js --report <path>');
|
|
218
|
-
console.log(' node test/analyze-results.js --compare <path1> <path2>');
|
|
219
|
-
process.exit(1);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (args[0] === '--compare') {
|
|
223
|
-
const analyzer = new TestResultsAnalyzer(args[1]);
|
|
224
|
-
analyzer.compare(args[2]);
|
|
225
|
-
} else if (args[0] === '--report') {
|
|
226
|
-
const analyzer = new TestResultsAnalyzer(args[1]);
|
|
227
|
-
analyzer.analyze();
|
|
228
|
-
} else {
|
|
229
|
-
console.error('Unknown command:', args[0]);
|
|
230
|
-
process.exit(1);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (require.main === module) {
|
|
235
|
-
main();
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
module.exports = { TestResultsAnalyzer };
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @fileoverview Dependency Cascade Tests
|
|
3
|
-
*
|
|
4
|
-
* Verifies that:
|
|
5
|
-
* 1. Only root computations (no dependencies) are picked up by the scheduler.
|
|
6
|
-
* 2. When a root computation completes, its dependents are scheduled via
|
|
7
|
-
* Cloud Tasks with the configured dependency gap.
|
|
8
|
-
* 3. Dispatcher does not return 503 for logical "blocked/impossible" states
|
|
9
|
-
* so Cloud Tasks will not spin on dependency waits.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { Orchestrator } = require('../../framework');
|
|
13
|
-
const config = require('../../config/bulltrackers.config');
|
|
14
|
-
const dispatcherModule = require('../../handlers/dispatcher');
|
|
15
|
-
|
|
16
|
-
// Simple CloudTasksClient mock
|
|
17
|
-
class MockCloudTasksClient {
|
|
18
|
-
constructor() {
|
|
19
|
-
this.createdTasks = [];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
queuePath(projectId, location, queueName) {
|
|
23
|
-
return `projects/${projectId}/locations/${location}/queues/${queueName}`;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async createTask(request) {
|
|
27
|
-
this.createdTasks.push(request);
|
|
28
|
-
return [{ name: `${request.parent}/tasks/mock-task` }];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function createMockResponse() {
|
|
33
|
-
let statusCode = 200;
|
|
34
|
-
let body = null;
|
|
35
|
-
return {
|
|
36
|
-
status(code) { statusCode = code; return this; },
|
|
37
|
-
json(data) { body = data; return this; },
|
|
38
|
-
send(data) { body = data; return this; },
|
|
39
|
-
getStatus() { return statusCode; },
|
|
40
|
-
getBody() { return body; }
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function testDispatcherNo503OnBlocked() {
|
|
45
|
-
console.log('\n=== Dependency Cascade: Dispatcher behaviour ===');
|
|
46
|
-
|
|
47
|
-
// Patch system.runComputation at runtime to simulate a blocked state
|
|
48
|
-
const system = require('../../core-api');
|
|
49
|
-
const originalRun = system.runComputation;
|
|
50
|
-
|
|
51
|
-
system.runComputation = async () => ({
|
|
52
|
-
name: 'popularinvestorriskassessment',
|
|
53
|
-
status: 'blocked',
|
|
54
|
-
reason: 'Waiting for: popularinvestorprofilemetrics'
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const res = createMockResponse();
|
|
58
|
-
await dispatcherModule.dispatcherHandler(
|
|
59
|
-
{
|
|
60
|
-
body: {
|
|
61
|
-
computationName: 'PopularInvestorRiskAssessment',
|
|
62
|
-
targetDate: '2026-01-20',
|
|
63
|
-
source: 'scheduled'
|
|
64
|
-
}
|
|
65
|
-
},
|
|
66
|
-
res
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
console.log(' Status:', res.getStatus());
|
|
70
|
-
console.log(' Body:', JSON.stringify(res.getBody()));
|
|
71
|
-
|
|
72
|
-
if (res.getStatus() === 503) {
|
|
73
|
-
throw new Error('Expected dispatcher NOT to return 503 for blocked status');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Restore original implementation
|
|
77
|
-
system.runComputation = originalRun;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
async function testOrchestratorDependencyScheduling() {
|
|
81
|
-
console.log('\n=== Dependency Cascade: Orchestrator scheduling ===');
|
|
82
|
-
|
|
83
|
-
// Limit manifest to just the Popular Investor chain for clarity
|
|
84
|
-
const localConfig = { ...config };
|
|
85
|
-
localConfig.computations = [
|
|
86
|
-
require('../../computations/PopularInvestorProfileMetrics'),
|
|
87
|
-
require('../../computations/PopularInvestorRiskAssessment')
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
// Mark test mode so we can identify this run in logs if needed
|
|
91
|
-
localConfig.testMode = { runId: 'dependency-cascade-test' };
|
|
92
|
-
|
|
93
|
-
const orch = new Orchestrator(localConfig, console);
|
|
94
|
-
|
|
95
|
-
// Inject mock CloudTasks client
|
|
96
|
-
orch.cloudTasksClient = new MockCloudTasksClient();
|
|
97
|
-
|
|
98
|
-
await orch.initialize();
|
|
99
|
-
|
|
100
|
-
// Simulate that today is 2026-01-20
|
|
101
|
-
const date = '2026-01-20';
|
|
102
|
-
|
|
103
|
-
// Execute only the root computation (PopularInvestorProfileMetrics).
|
|
104
|
-
const rootEntry = orch.manifest.find(e => e.originalName === 'PopularInvestorProfileMetrics');
|
|
105
|
-
if (!rootEntry) {
|
|
106
|
-
throw new Error('PopularInvestorProfileMetrics not found in manifest');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(' Running root computation:', rootEntry.originalName);
|
|
110
|
-
await orch.runSingle(rootEntry, date, { dryRun: true });
|
|
111
|
-
|
|
112
|
-
const tasks = orch.cloudTasksClient.createdTasks;
|
|
113
|
-
console.log(' Cloud Tasks created:', tasks.length);
|
|
114
|
-
|
|
115
|
-
if (tasks.length === 0) {
|
|
116
|
-
console.log(' NOTE: No dependent tasks were scheduled (expected if dependencies incomplete in status store).');
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const payload = JSON.parse(
|
|
121
|
-
Buffer.from(tasks[0].task.httpRequest.body, 'base64').toString('utf8')
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
console.log(' First task payload:', payload);
|
|
125
|
-
|
|
126
|
-
if (payload.source !== 'dependency') {
|
|
127
|
-
throw new Error('Expected dependent tasks to be marked with source="dependency"');
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async function main() {
|
|
132
|
-
console.log('╔════════════════════════════════════════════════════════════╗');
|
|
133
|
-
console.log('║ Computation System v2 - Dependency Cascade ║');
|
|
134
|
-
console.log('╚════════════════════════════════════════════════════════════╝');
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await testDispatcherNo503OnBlocked();
|
|
138
|
-
await testOrchestratorDependencyScheduling();
|
|
139
|
-
|
|
140
|
-
console.log('\nAll dependency cascade tests completed.\n');
|
|
141
|
-
} catch (err) {
|
|
142
|
-
console.error('\n❌ Dependency cascade test failed:', err);
|
|
143
|
-
process.exit(1);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (require.main === module) {
|
|
148
|
-
main();
|
|
149
|
-
}
|
|
150
|
-
|