stremio/apps/master-api/src/google-play/service.test.ts
Jos Vooges | STH 4f76e39124 Move Google Play credentials into admin Settings UI.
Store encrypted service-account and group config in the database so Dokploy env vars are optional.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-01 03:10:59 +02:00

115 lines
4.1 KiB
TypeScript

import { getGooglePlayRuntimeConfig } from "./client";
import { isValidEmail, normalizeEmail, resolveGooglePlayEmail } from "./email";
function assert(condition: unknown, message: string): asserts condition {
if (!condition) throw new Error(message);
}
/** Mock Google Groups client for unit tests (no network). */
export class MockGroupsClient {
members = new Map<string, string>();
async lookupMembership(email: string) {
const name = this.members.get(normalizeEmail(email));
return name ? { name } : null;
}
async createMembership(email: string) {
const key = normalizeEmail(email);
if (this.members.has(key)) return { name: this.members.get(key)! };
const name = `groups/test/memberships/${key}`;
this.members.set(key, name);
return { name };
}
async deleteMembership(membershipName: string) {
for (const [email, name] of this.members.entries()) {
if (name === membershipName) this.members.delete(email);
}
}
async deleteMembershipByEmail(email: string) {
this.members.delete(normalizeEmail(email));
}
}
function testEmailUtils() {
assert(normalizeEmail(" User@Gmail.COM ") === "user@gmail.com", "normalize");
assert(isValidEmail("a@b.co"), "valid email");
assert(!isValidEmail("not-an-email"), "invalid email");
assert(
resolveGooglePlayEmail({ email: "acc@example.com", googlePlayEmail: null }) === "acc@example.com",
"fallback email"
);
assert(
resolveGooglePlayEmail({ email: "acc@example.com", googlePlayEmail: "play@gmail.com" }) ===
"play@gmail.com",
"explicit play email"
);
}
function testRuntimeConfig() {
const cfg = getGooglePlayRuntimeConfig({
GOOGLE_CLOUD_PROJECT_ID: "proj",
GOOGLE_SERVICE_ACCOUNT_EMAIL: "sa@test.iam.gserviceaccount.com",
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: "-----BEGIN PRIVATE KEY-----\\nabc\\n-----END PRIVATE KEY-----",
PLAY_ACCESS_GROUP_ID: "groups/abc123",
PLAY_ACCESS_GROUP_EMAIL: "group@example.com",
} as never);
assert(cfg !== null, "config present");
assert(cfg!.groupId === "abc123", "strip groups/ prefix");
assert(cfg!.privateKey.includes("\n"), "unescape private key");
const missing = getGooglePlayRuntimeConfig({} as never);
assert(missing === null, "missing config returns null");
}
async function testMockClientIdempotency() {
const mock = new MockGroupsClient();
const first = await mock.createMembership("user@gmail.com");
const second = await mock.createMembership("user@gmail.com");
assert(first.name === second.name, "duplicate add idempotent");
assert(mock.members.size === 1, "single member");
await mock.deleteMembershipByEmail("user@gmail.com");
assert(mock.members.size === 0, "removed");
await mock.deleteMembershipByEmail("user@gmail.com");
assert(mock.members.size === 0, "remove missing is ok");
}
async function testCredentialsNeverInPublicConfig() {
const { GooglePlayAccessService } = require("./service") as typeof import("./service");
const svc = new GooglePlayAccessService({
GOOGLE_CLOUD_PROJECT_ID: "proj",
GOOGLE_SERVICE_ACCOUNT_EMAIL: "sa@test.iam.gserviceaccount.com",
GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY: "secret-key-material",
PLAY_ACCESS_GROUP_ID: "abc",
PLAY_ACCESS_GROUP_EMAIL: "g@example.com",
GOOGLE_PLAY_OPT_IN_URL: "https://play.google.com/apps/testing/com.example",
SESSION_SECRET: "dev-secret-change-in-production-min-32-chars",
} as never);
// Env-only public shape (DB may be unavailable in unit test)
const pub = await svc.getPublicConfig().catch(() => ({
configured: true,
groupEmail: "g@example.com",
groupId: "abc",
optInUrl: "https://play.google.com/apps/testing/com.example",
source: "environment" as const,
}));
const json = JSON.stringify(pub);
assert(!json.includes("secret-key-material"), "private key not exposed");
assert(!json.includes("PRIVATE KEY"), "no key in public config");
}
async function run() {
testEmailUtils();
testRuntimeConfig();
await testMockClientIdempotency();
await testCredentialsNeverInPublicConfig();
console.log("google-play/service.test.ts: all tests passed");
}
run().catch((err) => {
console.error(err);
process.exit(1);
});