-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.ts
More file actions
211 lines (179 loc) · 8.29 KB
/
test.ts
File metadata and controls
211 lines (179 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
/**
* ASO MCP Server — Manual Test Script
* Tests all tool data sources directly.
*
* Run: npx tsx test.ts
*/
import { searchApps, getAppDetails, getReviews, getSuggestions } from "./src/data-sources/app-store.js";
import { getScores, suggestKeywords } from "./src/data-sources/aso-scoring.js";
import { initCache, getFromCache, setCache, getCacheStats } from "./src/cache/sqlite-cache.js";
import { extractTitleKeywords, calculateCompetitiveScore, calculateOpportunityScore } from "./src/data-sources/custom-scoring.js";
const PASS = "\x1b[32m✓\x1b[0m";
const FAIL = "\x1b[31m✗\x1b[0m";
const INFO = "\x1b[36mℹ\x1b[0m";
const WARN = "\x1b[33m⚠\x1b[0m";
let passed = 0;
let failed = 0;
async function test(name: string, fn: () => Promise<void>) {
try {
await fn();
console.log(` ${PASS} ${name}`);
passed++;
} catch (err: any) {
console.log(` ${FAIL} ${name} — ${err.message}`);
failed++;
}
}
function assert(condition: boolean, msg: string) {
if (!condition) throw new Error(msg);
}
async function main() {
console.log("\n🔧 ASO MCP Server — Full Test Suite\n");
// ─── 1. Cache ───
console.log("📦 Cache Layer");
initCache();
await test("Cache init & stats", async () => {
const stats = getCacheStats();
assert(typeof stats.totalEntries === "number", "totalEntries should exist");
});
await test("Cache set & get", async () => {
setCache("test-key", "test-value", 60);
const val = getFromCache("test-key");
assert(val === "test-value", `Expected: test-value, Got: ${val}`);
});
// ─── 2. Custom Scoring ───
console.log("\n🧮 Custom Scoring");
await test("extractTitleKeywords", async () => {
const kws = extractTitleKeywords("Spotify: Muzik ve Podcast");
assert(kws.length > 0, "Keywords should be extracted");
assert(kws.includes("spotify"), `spotify should be found, got: ${kws}`);
});
await test("calculateCompetitiveScore", async () => {
const score = calculateCompetitiveScore([
{ rating: 4.5, reviews: 50000, free: true },
{ rating: 4.2, reviews: 30000, free: true },
{ rating: 3.8, reviews: 10000, free: false },
]);
assert(score >= 0 && score <= 10, `Score should be 0-10: ${score}`);
console.log(` ${INFO} Competitive score: ${score.toFixed(1)}`);
});
await test("calculateOpportunityScore", async () => {
const high = calculateOpportunityScore(8, 3);
const low = calculateOpportunityScore(2, 9);
assert(high > low, `High opportunity > low opportunity: ${high} vs ${low}`);
console.log(` ${INFO} High opp: ${high.toFixed(1)}, Low opp: ${low.toFixed(1)}`);
});
// ─── 3. App Store Scraper ───
console.log("\n🍎 App Store Scraper");
await test("searchApps — 'fitness' TR", async () => {
const apps = await searchApps("fitness", "tr", 5);
assert(Array.isArray(apps), "Should return array");
assert(apps.length > 0, "Should have results");
console.log(` ${INFO} ${apps.length} apps found — First: "${(apps[0] as any).title}"`);
});
await test("getAppDetails — Spotify (bundle ID)", async () => {
const app = await getAppDetails("com.spotify.client", "tr");
assert(app.title != null, "title should exist");
assert(app.score > 0, "rating should be > 0");
console.log(` ${INFO} ${app.title} — Rating: ${app.score} — ${app.reviews} reviews`);
});
await test("getAppDetails — numeric ID (Spotify: 324684580)", async () => {
const app = await getAppDetails("324684580", "tr");
assert(app.title != null, "title should exist");
console.log(` ${INFO} ${app.title}`);
});
await test("getReviews — Spotify TR", async () => {
const reviews = await getReviews(324684580, "tr", 1);
assert(Array.isArray(reviews), "Should return array");
console.log(` ${INFO} ${reviews.length} reviews fetched`);
if (reviews.length > 0) {
const r = reviews[0];
console.log(` ${INFO} Sample: "${(r.title || r.text || "").slice(0, 60)}..." Rating:${r.score}`);
}
});
await test("getSuggestions — 'music'", async () => {
const suggestions = await getSuggestions("music");
assert(Array.isArray(suggestions), "Should return array");
assert(suggestions.length > 0, "Should have suggestions");
console.log(` ${INFO} ${suggestions.length} suggestions: ${suggestions.slice(0, 5).join(", ")}`);
});
// ─── 4. ASO Scoring (with fallback) ───
console.log("\n📊 ASO Scoring (aso package or fallback)");
await test("getScores — 'fitness tracker' US", async () => {
const scores = await getScores("fitness tracker", "us");
assert(typeof scores.traffic === "number", "traffic should exist");
assert(typeof scores.difficulty === "number", "difficulty should exist");
assert(scores.traffic >= 0, "traffic should be >= 0");
assert(scores.difficulty >= 0, "difficulty should be >= 0");
console.log(` ${INFO} Traffic: ${scores.traffic}, Difficulty: ${scores.difficulty}`);
});
await test("getScores — 'muzik' TR", async () => {
const scores = await getScores("muzik", "tr");
assert(typeof scores.traffic === "number", "traffic should exist");
console.log(` ${INFO} Traffic: ${scores.traffic}, Difficulty: ${scores.difficulty}`);
});
await test("getScores — 'photo editor' US", async () => {
const scores = await getScores("photo editor", "us");
assert(scores.traffic > 0, "traffic > 0 expected");
console.log(` ${INFO} Traffic: ${scores.traffic}, Difficulty: ${scores.difficulty}`);
});
await test("suggestKeywords — Spotify (competition)", async () => {
const keywords = await suggestKeywords("324684580", "competition", "tr", 10);
assert(Array.isArray(keywords), "Should return array");
console.log(` ${INFO} ${keywords.length} keywords suggested: ${keywords.slice(0, 5).join(", ")}`);
});
// ─── 5. Integration: Tool Scenarios ───
console.log("\n🔗 Integration Scenarios");
await test("Scenario: Keyword research (search + score)", async () => {
const keyword = "meditation";
const apps = await searchApps(keyword, "us", 5);
const scores = await getScores(keyword, "us");
assert(apps.length > 0, "Should have app results");
assert(typeof scores.traffic === "number", "Should have scores");
const competitive = calculateCompetitiveScore(
apps.map((a: any) => ({
rating: a.score || 0,
reviews: a.reviews || 0,
free: a.free ?? true,
}))
);
console.log(` ${INFO} "${keyword}": ${apps.length} apps, traffic=${scores.traffic}, difficulty=${scores.difficulty}, competitive=${competitive.toFixed(1)}`);
});
await test("Scenario: App detail + review analysis", async () => {
const app = await getAppDetails("com.spotify.client", "us");
const reviews = await getReviews(app.id, "us", 1);
assert(app.title != null, "App info should exist");
assert(reviews.length > 0, "Should have reviews");
// Simple sentiment
let pos = 0, neg = 0;
for (const r of reviews) {
if (r.score >= 4) pos++;
else if (r.score <= 2) neg++;
}
console.log(` ${INFO} ${app.title}: ${reviews.length} reviews, ${pos} positive, ${neg} negative`);
});
await test("Scenario: Keyword gap (two app comparison)", async () => {
const app1 = await getAppDetails("com.spotify.client", "us");
const app2 = await getAppDetails("com.apple.music", "us");
const kw1 = extractTitleKeywords(app1.title || "");
const kw2 = extractTitleKeywords(app2.title || "");
const set1 = new Set(kw1);
const set2 = new Set(kw2);
const shared = kw1.filter((k: string) => set2.has(k));
const onlyApp1 = kw1.filter((k: string) => !set2.has(k));
const onlyApp2 = kw2.filter((k: string) => !set1.has(k));
console.log(` ${INFO} ${app1.title} vs ${app2.title}`);
console.log(` ${INFO} Shared: [${shared.join(", ")}] | Only 1: [${onlyApp1.join(", ")}] | Only 2: [${onlyApp2.join(", ")}]`);
});
// ─── Result ───
console.log(`\n${"─".repeat(50)}`);
console.log(` ${PASS} ${passed} tests passed`);
if (failed > 0) console.log(` ${FAIL} ${failed} tests failed`);
else console.log(` 🎉 All tests passed!`);
console.log(`${"─".repeat(50)}\n`);
process.exit(failed > 0 ? 1 : 0);
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});