feat(test): add localhost testing support for prefetch

- Add serverMode configuration to test-user-zero config
  - Supports: localhost, staging, production, mock, custom
  - Auto-detects platform (Android/iOS/Web) for localhost URLs
  - Android emulator uses 10.0.2.2 for host machine localhost

- Add getApiServerUrl() helper function
  - Returns correct URL based on serverMode and platform
  - Handles Android emulator special case (10.0.2.2)

- Update TestUserZeroAPI to respect serverMode
  - Checks mock mode before making network calls
  - Uses getApiServerUrl() for base URL resolution
  - Allows runtime URL switching via setBaseUrl()

- Add localhost testing configuration
  - Configurable port and HTTPS settings
  - Development mode headers support

- Create localhost testing guide
  - Step-by-step setup instructions
  - Platform-specific localhost addresses explained
  - Quick test API server example
  - Troubleshooting common issues
  - Monitoring prefetch execution commands

- Update Capacitor config to use getApiServerUrl()
  - Fixes breaking change from api.server removal

This enables testing prefetch functionality with a local development
API server running on localhost, perfect for development and debugging
of the 5-minute prefetch scheduling feature.
This commit is contained in:
Matthew Raymer
2025-10-29 12:01:05 +00:00
parent fd4ddcbd60
commit 1bf39fd1f7
3 changed files with 381 additions and 8 deletions

View File

@@ -18,7 +18,8 @@ const config: CapacitorConfig = {
timesafariConfig: {
activeDid: TEST_USER_ZERO_CONFIG.identity.did,
endpoints: {
projectsLastUpdated: `${TEST_USER_ZERO_CONFIG.api.server}${TEST_USER_ZERO_CONFIG.api.starsEndpoint}`
// Use getApiServerUrl() to get correct URL based on serverMode
projectsLastUpdated: `${TEST_USER_ZERO_CONFIG.getApiServerUrl()}${TEST_USER_ZERO_CONFIG.api.starsEndpoint}`
},
starredProjectsConfig: {
enabled: true,

View File

@@ -19,8 +19,27 @@ export const TEST_USER_ZERO_CONFIG = {
// API Configuration
api: {
// Use staging API server for testing
server: "https://api-staging.timesafari.com",
// API server selection:
// - "localhost": Use 10.0.2.2:PORT (Android emulator special alias for host localhost)
// - "staging": Use staging API server
// - "production": Use production API server
// - "mock": Use mock responses (no network calls)
// - "custom": Use servers.custom URL
serverMode: "mock" as "localhost" | "staging" | "production" | "mock" | "custom",
// Server URLs for different modes
servers: {
// Android emulator: use 10.0.2.2 to access host machine's localhost
// iOS simulator: use localhost or 127.0.0.1
localhost: {
android: "http://10.0.2.2:3000", // Default local dev server port
ios: "http://localhost:3000",
web: "http://localhost:3000"
},
staging: "https://api-staging.timesafari.com",
production: "https://api.timesafari.com",
custom: "" // Set this if using custom URL
},
// Stars querying endpoint (from crowd-master endorserServer.ts)
starsEndpoint: "/api/v2/report/plansLastUpdatedBetween",
@@ -29,6 +48,36 @@ export const TEST_USER_ZERO_CONFIG = {
jwtExpirationMinutes: 1, // Short-lived tokens like crowd-master
jwtAlgorithm: "HS256"
},
/**
* Get current API server URL based on mode and platform
* Call this function instead of accessing api.server directly
*/
getApiServerUrl(): string {
if (TEST_USER_ZERO_CONFIG.api.serverMode === "mock") {
return "mock://localhost"; // Not used, but needed for type checking
}
if (TEST_USER_ZERO_CONFIG.api.serverMode === "localhost") {
// Detect platform and use appropriate localhost address
if (typeof window !== "undefined") {
const isAndroid = /Android/i.test(navigator.userAgent);
const isIOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
if (isAndroid) {
return TEST_USER_ZERO_CONFIG.api.servers.localhost.android;
} else if (isIOS) {
return TEST_USER_ZERO_CONFIG.api.servers.localhost.ios;
} else {
return TEST_USER_ZERO_CONFIG.api.servers.localhost.web;
}
}
return TEST_USER_ZERO_CONFIG.api.servers.localhost.android; // Default to Android for Capacitor
}
if (TEST_USER_ZERO_CONFIG.api.serverMode === "custom" && TEST_USER_ZERO_CONFIG.api.servers.custom) {
return TEST_USER_ZERO_CONFIG.api.servers.custom;
}
return TEST_USER_ZERO_CONFIG.api.servers[TEST_USER_ZERO_CONFIG.api.serverMode] as string;
},
// Test Starred Projects (mock data for testing)
starredProjects: {
@@ -61,8 +110,23 @@ export const TEST_USER_ZERO_CONFIG = {
// Testing Configuration
testing: {
// Enable mock responses for offline testing
// When true, API calls return mock data instead of making network requests
enableMockResponses: true,
// Localhost testing configuration
localhost: {
// Port your local dev server runs on
port: 3000,
// Enable HTTPS for localhost (requires self-signed cert in emulator)
useHttps: false,
// Additional headers for localhost requests
headers: {
"X-Development-Mode": "true"
}
},
// Network timeouts
timeoutMs: 30000,
retryAttempts: 3,
@@ -160,11 +224,22 @@ export class TestUserZeroAPI {
private baseUrl: string;
private jwt: string;
constructor(baseUrl: string = TEST_USER_ZERO_CONFIG.api.server) {
this.baseUrl = baseUrl;
constructor(baseUrl?: string) {
// Use provided URL, or get from config based on serverMode
this.baseUrl = baseUrl || (TEST_USER_ZERO_CONFIG.api.serverMode === "mock"
? "mock://localhost"
: TEST_USER_ZERO_CONFIG.getApiServerUrl());
this.jwt = generateTestJWT();
}
/**
* Set API server URL (useful for switching between localhost, staging, etc.)
*/
setBaseUrl(url: string): void {
this.baseUrl = url;
console.log("🔧 API base URL updated to:", url);
}
/**
* Query starred projects for changes
* Mimics crowd-master getStarredProjectsWithChanges function
@@ -173,15 +248,19 @@ export class TestUserZeroAPI {
starredPlanIds: string[],
afterId?: string
): Promise<typeof MOCK_STARRED_PROJECTS_RESPONSE> {
if (TEST_USER_ZERO_CONFIG.testing.enableMockResponses) {
// Check if we should use mock responses
const useMock = TEST_USER_ZERO_CONFIG.testing.enableMockResponses ||
TEST_USER_ZERO_CONFIG.api.serverMode === "mock";
if (useMock) {
// Return mock data for offline testing
console.log("🧪 Using mock starred projects response");
return MOCK_STARRED_PROJECTS_RESPONSE;
}
// Real API call (when mock is disabled)
const url = `${this.baseUrl}${TEST_USER_ZERO_CONFIG.api.starsEndpoint}`;
const baseUrl = this.baseUrl || TEST_USER_ZERO_CONFIG.getApiServerUrl();
const url = `${baseUrl}${TEST_USER_ZERO_CONFIG.api.starsEndpoint}`;
const headers = {
'Authorization': `Bearer ${this.jwt}`,
'Content-Type': 'application/json',