fix(test): use valid URI format for plan handle IDs

- Update plan handle ID format to match TimeSafari specification
  - Default format: https://endorser.ch/entity/{26-char-ULID}
  - ULID: 26 characters, Crockford base32 encoded
  - Validates RFC 3986 URI format

- Add ULID generation functions
  - generateULID() creates 26-character Crockford base32 strings
  - generateValidPlanHandleId() creates full URI format IDs
  - Auto-generates valid IDs for testing

- Update DEFAULT_TEST_PROJECT_IDS
  - Now generates valid URI format IDs automatically
  - Removes placeholder warnings (IDs are now valid format)

- Add URI validation to seed scripts
  - Validates plan IDs match RFC 3986 URI format
  - Error messages with format examples
  - Blocks seeding with invalid formats

- Update test-user-zero.ts config
  - Auto-generates valid URI format plan IDs
  - Clear documentation of required format
  - Note that real IDs from database should replace test IDs

- Update documentation
  - Document default URI format specification
  - Explain ULID structure and encoding
  - Show examples of valid formats

This ensures all test project IDs match the actual TimeSafari plan
handle ID format, preventing validation errors during prefetch testing.
This commit is contained in:
Matthew Raymer
2025-10-29 12:20:55 +00:00
parent 7a19a56ea2
commit 848387b532
4 changed files with 128 additions and 73 deletions

View File

@@ -11,6 +11,29 @@
const http = require('http');
/**
* Generate a valid ULID (26 characters, Crockford base32)
* Simplified version for testing - not cryptographically secure
*/
function generateULID() {
const timestamp = Date.now();
// Crockford base32 alphabet (0-9, A-Z excluding I, L, O, U)
const alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
const timestampPart = timestamp.toString(36).toUpperCase().padStart(10, '0');
const randomPart = Array.from({ length: 16 }, () =>
alphabet[Math.floor(Math.random() * alphabet.length)]
).join('');
return (timestampPart + randomPart).substring(0, 26);
}
/**
* Generate a valid plan handle ID in URI format
* Default format: https://endorser.ch/entity/{ULID}
*/
function generateValidPlanHandleId() {
return `https://endorser.ch/entity/${generateULID()}`;
}
/**
* Generate a test JWT ID with timestamp prefix for sorting
*/
@@ -71,26 +94,27 @@ function generateTestProjectWithClaim(handleId, index = 0) {
/**
* Default test project IDs from config
*
* NOTE: These are placeholder IDs. For real testing, replace with valid plan handle IDs
* from your TimeSafari database. Valid plan IDs can be obtained by:
* Plan handle IDs must be valid URIs (RFC 3986 format):
* - Default format: https://endorser.ch/entity/{26-char-ULID}
* - ULID is 26 characters, Crockford base32 encoded
* - Any valid URI scheme is accepted: http://, https://, did:, etc.
*
* 1. Creating a project in the TimeSafari app and noting its handleId
* 2. Querying your local database for existing plan handleIds
* 3. Using the plan handleIds from your starred projects in account settings
* For real testing, use actual plan handle IDs from your TimeSafari database.
* To get valid IDs:
* 1. Create projects in TimeSafari app and note handleId from API responses
* 2. Query your local database: SELECT handleId FROM plans LIMIT 5
* 3. Check starred projects: settings.starredPlanHandleIds
*
* Plan handle IDs typically follow the format used by your TimeSafari backend.
* If your localhost has no projects, you may need to create test projects first
* or use the IDs from your staging/production environment.
* The IDs below are auto-generated valid URIs for testing structure only.
* Replace with real IDs from your database for actual prefetch testing.
*/
const DEFAULT_TEST_PROJECT_IDS = [
// Replace these with actual plan handle IDs from your TimeSafari setup
// Example formats (check your actual database for real IDs):
// - UUID format: "550e8400-e29b-41d4-a716-446655440000"
// - Hash format: "abc123def456"
// - Or whatever format your TimeSafari API uses
"PLACEHOLDER_ID_1",
"PLACEHOLDER_ID_2",
"PLACEHOLDER_ID_3"
// Auto-generate valid format IDs for testing
generateValidPlanHandleId(),
generateValidPlanHandleId(),
generateValidPlanHandleId(),
generateValidPlanHandleId(),
generateValidPlanHandleId()
];
/**
@@ -220,14 +244,11 @@ if (require.main === module) {
const filename = args[1] || 'test-projects.json';
const projectIds = args[2] ? args[2].split(',') : DEFAULT_TEST_PROJECT_IDS;
if (projectIds.some(id => id.startsWith('PLACEHOLDER'))) {
console.warn('⚠️ WARNING: Using placeholder IDs. Replace with real plan handle IDs from your TimeSafari database.');
console.warn(' To get valid IDs:');
console.warn(' 1. Create projects in TimeSafari app');
console.warn(' 2. Query your database for plan handleIds');
console.warn(' 3. Use IDs from starred projects in account settings');
console.warn('');
}
// Warn if using auto-generated IDs (they'll be valid URIs but not from real database)
console.log('📝 Using provided plan handle IDs.');
console.log(' Note: For real testing, use IDs from your TimeSafari database.');
console.log(' Valid format: https://endorser.ch/entity/{ULID} or any valid URI');
console.log('');
exportToJSON(filename, projectIds);
}
@@ -238,10 +259,16 @@ if (require.main === module) {
const apiUrl = args[1] || 'http://localhost:3000';
const projectIds = args[2] ? args[2].split(',') : DEFAULT_TEST_PROJECT_IDS;
if (projectIds.some(id => id.startsWith('PLACEHOLDER'))) {
console.error('❌ ERROR: Cannot seed with placeholder IDs. Please provide valid plan handle IDs.');
console.error(' Usage: node scripts/seed-test-projects.js seed <apiUrl> "<id1>,<id2>,<id3>"');
console.error(' Example: node scripts/seed-test-projects.js seed http://localhost:3000 "550e8400-e29b-41d4-a716-446655440000,abc123def456"');
// Validate plan IDs are valid URIs (RFC 3986)
const invalidIds = projectIds.filter(id => !id.match(/^[A-Za-z][A-Za-z0-9+.-]+:/));
if (invalidIds.length > 0) {
console.error('❌ ERROR: Invalid plan handle ID format.');
console.error(' Plan handle IDs must be valid URIs (RFC 3986).');
console.error(' Format: https://endorser.ch/entity/{ULID}');
console.error(' Example: https://endorser.ch/entity/01GQBE7Q0RQQAGJMEEW6RSGKTF');
console.error(` Invalid IDs: ${invalidIds.join(', ')}`);
console.error('');
console.error(' Usage: node scripts/seed-test-projects.js seed <apiUrl> "<uri1>,<uri2>,<uri3>"');
process.exit(1);
}
@@ -262,9 +289,9 @@ if (require.main === module) {
{
const projectIds = args[1] ? args[1].split(',') : DEFAULT_TEST_PROJECT_IDS;
if (projectIds.some(id => id.startsWith('PLACEHOLDER'))) {
console.warn('⚠️ WARNING: Using placeholder IDs. These need to be replaced with real plan handle IDs.');
}
console.log('📝 Generated test projects with valid URI format handle IDs.');
console.log(' Format: https://endorser.ch/entity/{ULID}');
console.log(' For real testing, replace with IDs from your TimeSafari database.');
const projects = generateAllTestProjects(projectIds);
console.log(JSON.stringify({ data: projects }, null, 2));