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:
@@ -62,14 +62,31 @@ cd /home/matthew/projects/timesafari/crowd-master
|
||||
|
||||
## Plan Handle ID Format
|
||||
|
||||
Plan handle IDs can have different formats depending on your TimeSafari backend:
|
||||
Plan handle IDs **must be valid URIs** (RFC 3986 format).
|
||||
|
||||
- **UUID format**: `550e8400-e29b-41d4-a716-446655440000`
|
||||
- **Hash format**: `abc123def456789`
|
||||
- **Sequential**: `plan_001`, `plan_002`
|
||||
- **Custom format**: Whatever your backend generates
|
||||
### Default Format (System-Generated)
|
||||
|
||||
**Important**: Check your actual TimeSafari API/database to determine the correct format.
|
||||
**`https://endorser.ch/entity/{ULID}`**
|
||||
|
||||
- **ULID**: 26-character string, Crockford base32 encoded
|
||||
- **Example**: `https://endorser.ch/entity/01GQBE7Q0RQQAGJMEEW6RSGKTF`
|
||||
|
||||
### Custom Global URIs
|
||||
|
||||
Any valid URI with a scheme is accepted:
|
||||
- `https://endorser.ch/entity/01GQBE7Q0RQQAGJMEEW6RSGKTF` (default)
|
||||
- `http://example.com/project/123` (custom)
|
||||
- `did:ethr:0x1234...` (DID format)
|
||||
- Any URI matching: `^[A-Za-z][A-Za-z0-9+.-]+:`
|
||||
|
||||
**Validation**: The format must match RFC 3986 URI specification.
|
||||
|
||||
### ULID Format Details
|
||||
|
||||
- **Length**: Exactly 26 characters
|
||||
- **Encoding**: Crockford base32 (0-9, A-Z excluding I, L, O, U)
|
||||
- **Structure**: Timestamp + random component
|
||||
- **Purpose**: Globally unique, lexicographically sortable identifiers
|
||||
|
||||
## Updating Test Configuration
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -38,33 +38,34 @@ app.use((req, res, next) => {
|
||||
* Seed test projects on startup
|
||||
*/
|
||||
function seedProjects(projectIds = DEFAULT_TEST_PROJECT_IDS) {
|
||||
// Check if using placeholder IDs
|
||||
if (projectIds.some(id => id.startsWith('PLACEHOLDER'))) {
|
||||
console.warn('');
|
||||
console.warn('⚠️ WARNING: Using placeholder plan handle IDs!');
|
||||
console.warn(' The test server will work, but you need to:');
|
||||
console.warn(' 1. Get valid plan handle IDs from your TimeSafari database');
|
||||
console.warn(' 2. Update test-user-zero.ts with real plan IDs');
|
||||
console.warn(' 3. Or provide valid IDs via command line:');
|
||||
console.warn(' node scripts/test-api-server-with-seed.js [port] "id1,id2,id3"');
|
||||
console.warn('');
|
||||
console.warn(' For now, the server will use placeholder IDs for testing structure only.');
|
||||
console.warn('');
|
||||
// Validate plan IDs are valid URIs
|
||||
const invalidIds = projectIds.filter(id => !id.match(/^[A-Za-z][A-Za-z0-9+.-]+:/));
|
||||
if (invalidIds.length > 0) {
|
||||
console.error('');
|
||||
console.error('❌ ERROR: Invalid plan handle ID format!');
|
||||
console.error(' Plan handle IDs must be valid URIs (RFC 3986).');
|
||||
console.error(' Expected format: https://endorser.ch/entity/{ULID}');
|
||||
console.error(` Invalid IDs: ${invalidIds.join(', ')}`);
|
||||
console.error('');
|
||||
console.error(' Valid examples:');
|
||||
console.error(' https://endorser.ch/entity/01GQBE7Q0RQQAGJMEEW6RSGKTF');
|
||||
console.error(' http://example.com/project/123');
|
||||
console.error(' did:ethr:0x1234...');
|
||||
console.error('');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
seededProjects = generateAllTestProjects(projectIds);
|
||||
console.log(`🌱 Seeded ${seededProjects.length} test projects:`);
|
||||
console.log(`🌱 Seeded ${seededProjects.length} test projects with valid URI format:`);
|
||||
seededProjects.forEach((project, index) => {
|
||||
console.log(` ${index + 1}. ${project.planSummary.handleId} - ${project.planSummary.name}`);
|
||||
});
|
||||
|
||||
if (projectIds.some(id => id.startsWith('PLACEHOLDER'))) {
|
||||
console.log('');
|
||||
console.log('📝 To use real plan IDs, update:');
|
||||
console.log(' test-apps/daily-notification-test/src/config/test-user-zero.ts');
|
||||
console.log(' starredProjects.planIds = ["your", "real", "plan", "ids"]');
|
||||
console.log('');
|
||||
}
|
||||
console.log('');
|
||||
console.log('📝 Note: These are auto-generated test IDs.');
|
||||
console.log(' For testing with real projects, use IDs from your TimeSafari database.');
|
||||
console.log(' Update: test-apps/daily-notification-test/src/config/test-user-zero.ts');
|
||||
console.log('');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -81,27 +81,37 @@ export const TEST_USER_ZERO_CONFIG = {
|
||||
|
||||
// Test Starred Projects (mock data for testing)
|
||||
starredProjects: {
|
||||
// IMPORTANT: Replace these with valid plan handle IDs from your TimeSafari database
|
||||
// IMPORTANT: 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 (e.g., 01GQBE7Q0RQQAGJMEEW6RSGKTF)
|
||||
// - Any valid URI scheme is accepted: http://, https://, did:, etc.
|
||||
//
|
||||
// To get valid plan IDs:
|
||||
// 1. Create projects in TimeSafari app and note their handleId from API responses
|
||||
// 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 in account settings: settings.starredPlanHandleIds
|
||||
// 3. Check starred projects: settings.starredPlanHandleIds
|
||||
// 4. See docs/getting-valid-plan-ids.md for detailed instructions
|
||||
//
|
||||
// Plan handle IDs can be:
|
||||
// - UUIDs: "550e8400-e29b-41d4-a716-446655440000"
|
||||
// - Hashes: "abc123def456789"
|
||||
// - Or any format your TimeSafari backend uses
|
||||
//
|
||||
// For localhost testing with no projects, you can:
|
||||
// - Use the test API server: node scripts/test-api-server-with-seed.js
|
||||
// - Or create test projects first, then use their IDs here
|
||||
planIds: [
|
||||
"PLACEHOLDER_ID_1", // Replace with real plan handle ID
|
||||
"PLACEHOLDER_ID_2", // Replace with real plan handle ID
|
||||
"PLACEHOLDER_ID_3" // Replace with real plan handle ID
|
||||
],
|
||||
// These are auto-generated valid URIs for testing structure.
|
||||
// Replace with real IDs from your TimeSafari database for actual testing.
|
||||
planIds: ((): string[] => {
|
||||
// Generate valid URI format IDs for testing
|
||||
const generateULID = (): string => {
|
||||
const alphabet = '0123456789ABCDEFGHJKMNPQRSTVWXYZ';
|
||||
const timestamp = Date.now().toString(36).toUpperCase().padStart(10, '0');
|
||||
const random = Array.from({ length: 16 }, () =>
|
||||
alphabet[Math.floor(Math.random() * alphabet.length)]
|
||||
).join('');
|
||||
return (timestamp + random).substring(0, 26);
|
||||
};
|
||||
|
||||
return [
|
||||
`https://endorser.ch/entity/${generateULID()}`,
|
||||
`https://endorser.ch/entity/${generateULID()}`,
|
||||
`https://endorser.ch/entity/${generateULID()}`
|
||||
];
|
||||
})(),
|
||||
|
||||
// Last acknowledged JWT ID (for pagination testing)
|
||||
lastAckedJwtId: "1704067200_abc123_def45678"
|
||||
|
||||
Reference in New Issue
Block a user