diff --git a/test-scripts/new_flow.py b/test-scripts/new_flow.py index 535ddeb..268c420 100644 --- a/test-scripts/new_flow.py +++ b/test-scripts/new_flow.py @@ -54,6 +54,7 @@ from jwcrypto import jwk import asyncio from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import ec +from pathlib import Path # Constants DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'" # Custom derivation path for TimeSafari @@ -61,6 +62,16 @@ API_SERVER = "https://test-api.endorser.ch" # Endorser API endpoint ENDORSER_DID = "did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F" # Keep original case ENDORSER_PRIVATE_KEY = "2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b" +# Create .generated directory if it doesn't exist +GENERATED_DIR = Path('.generated') + +def initialize_directories(): + """Create necessary directories for storing generated files.""" + try: + GENERATED_DIR.mkdir(exist_ok=True) + except Exception as e: + print(f"Error creating .generated directory: {e}") + raise def derive_address( mnemonic: str, @@ -98,6 +109,21 @@ def derive_address( print(f" Private Key: {private_hex[:8]}...") print(f" Public Key: {public_hex[:8]}...") + # Save derivation data + derivation_data = { + "mnemonic": mnemonic, + "derivation_path": derivation_path, + "address": address, + "private_key": private_hex, + "public_key": public_hex + } + try: + GENERATED_DIR.mkdir(exist_ok=True) + with open(GENERATED_DIR / 'key_derivation.json', 'w') as f: + json.dump(derivation_data, f, indent=2) + except Exception as e: + print(f"Error saving derivation data: {e}") + return address, private_hex, public_hex, derivation_path def new_identifier( @@ -165,13 +191,16 @@ def initialize_account() -> Dict[str, Any]: # Create DID identifier identity = new_identifier(address, public_hex, private_hex, derivation_path) - # Format account data + # Save account data account_data = { "did": identity["did"], "identity": identity, "mnemonic": mnemonic, "derivation_path": derivation_path } + with open(GENERATED_DIR / 'account_init.json', 'w') as f: + json.dump(account_data, f, indent=2) + print("\nAccount initialized:") print(json.dumps(account_data, indent=2)) print() @@ -328,6 +357,15 @@ async def register( response_data = response.json() if response.status_code == 200 and response_data.get("success", {}).get("handleId"): + # Save registration data + registration_data = { + "active_did": active_did, + "jwt_token": jwt_token, + "response": response_data + } + with open(GENERATED_DIR / 'registration.json', 'w') as f: + json.dump(registration_data, f, indent=2) + return { "success": True, "handleId": response_data["success"]["handleId"], @@ -393,15 +431,69 @@ async def fetch_claim( headers={ 'Authorization': f'Bearer {auth_token}', 'Content-Type': 'application/json' - } + }, + timeout=30 # 30 second timeout ) - return response.json() + claim_data = response.json() + + # Save claim data + try: + with open(GENERATED_DIR / 'claim_details.json', 'w', encoding='utf-8') as f: + json.dump({ + 'claim_id': claim_id, + 'active_did': active_did, + 'response': claim_data + }, f, indent=2) + except Exception as e: + print(f"Error saving claim data: {e}") + + return claim_data except requests.RequestException as e: print("\nError fetching claim:") print(f"Error: {str(e)}") + # Save error state + try: + with open( + GENERATED_DIR / 'claim_details.json', + 'w', encoding='utf-8') as f: + json.dump({ + 'claim_id': claim_id, + 'active_did': active_did, + 'error': str(e) + }, f, indent=2) + except Exception as write_err: + print(f"Error saving claim error: {write_err}") raise +async def generate_test_env(): + """Generate test environment data for deeplink testing""" + test_env_data = { + 'CONTACT1_DID': active_did, + 'CONTACT1_KEY': private_key_hex, + 'CONTACT2_DID': recipient_did, + 'CONTACT2_KEY': recipient_key, + 'ISSUER_DID': issuer_did, + 'ISSUER_KEY': issuer_key, + 'TEST_JWT': jwt_token, + 'API_SERVER': API_SERVER + } + + # Write test environment variables + with open(GENERATED_DIR / 'test-env.sh', 'w', encoding='utf-8') as f: + for key, value in test_env_data.items(): + f.write(f'export {key}="{value}"\n') + + # Write test contacts data + contacts_data = { + 'contacts': [ + {'did': active_did, 'name': 'Test Contact 1'}, + {'did': recipient_did, 'name': 'Test Contact 2'} + ] + } + with open(GENERATED_DIR / 'contacts.json', 'w', encoding='utf-8') as f: + json.dump(contacts_data, f, indent=2) + # Main execution async def main(): """ @@ -442,28 +534,38 @@ async def main(): Registration result: {"success": true} ``` """ - # Step 1: Create a new DID - identity = initialize_account() - active_did = identity["did"] - private_key_hex = identity["keys"][0]["privateKeyHex"] - - # Step 2: Register the DID - result = await register(active_did, private_key_hex) - print("Registration result:", result) - - # Step 3: If registration successful, fetch claim details - if result.get("success") and result.get("claimId"): - print("\nFetching claim details...") - try: - claim_details = await fetch_claim( - result["claimId"], - active_did, - private_key_hex - ) - print("\nClaim Details:") - print(json.dumps(claim_details, indent=2)) - except Exception as e: - print(f"Error fetching claim: {str(e)}") + # Initialize directories first + initialize_directories() + + try: + # Step 1: Create a new DID + identity = initialize_account() + active_did = identity["did"] + private_key_hex = identity["keys"][0]["privateKeyHex"] + + # Step 2: Register the DID + result = await register(active_did, private_key_hex) + print("Registration result:", result) + + # Step 3: If registration successful, fetch claim details + if result.get("success") and result.get("claimId"): + print("\nFetching claim details...") + try: + claim_details = await fetch_claim( + result["claimId"], + active_did, + private_key_hex + ) + print("\nClaim Details:") + print(json.dumps(claim_details, indent=2)) + except Exception as e: + print(f"Error fetching claim: {str(e)}") + + # Step 4: Generate test environment data + await generate_test_env() + + except Exception as e: + print(f"Error: {str(e)}") if __name__ == "__main__": asyncio.run(main()) diff --git a/test-scripts/new_flow.ts b/test-scripts/new_flow.ts index c51dbf2..6c55659 100644 --- a/test-scripts/new_flow.ts +++ b/test-scripts/new_flow.ts @@ -10,6 +10,8 @@ import { HDNode } from '@ethersproject/hdnode'; import { Wallet } from '@ethersproject/wallet'; import { createJWT, ES256KSigner, SimpleSigner } from 'did-jwt'; import axios from 'axios'; +import { mkdir, writeFile } from 'fs/promises'; +import { join } from 'path'; // Constants const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'"; @@ -32,6 +34,19 @@ interface IIdentifier { services: any[]; } +// Create .generated directory if it doesn't exist +const GENERATED_DIR = '.generated'; + +// Initialize directory creation +async function initializeDirectories() { + try { + await mkdir(GENERATED_DIR, { recursive: true }); + } catch (err) { + console.error('Error creating .generated directory:', err); + throw err; + } +} + /** * Generate a new mnemonic seed phrase */ @@ -62,6 +77,24 @@ async function deriveAddress( console.log(` Private Key: ${privateHex.substring(0,8)}...`); console.log(` Public Key: ${publicHex.substring(0,8)}...`); + // Save derivation data + const derivationData = { + mnemonic, + derivationPath, + address, + privateKey: privateHex, + publicKey: publicHex + }; + try { + await mkdir(GENERATED_DIR, { recursive: true }); + await writeFile( + join(GENERATED_DIR, 'key_derivation.json'), + JSON.stringify(derivationData, null, 2) + ); + } catch (err) { + console.error('Error saving derivation data:', err); + } + return [address, privateHex, publicHex, derivationPath]; } @@ -111,6 +144,12 @@ async function initializeAccount(): Promise { console.log(JSON.stringify(accountData, null, 2)); console.log(); + // Save account data + await writeFile( + join(GENERATED_DIR, 'account_init.json'), + JSON.stringify(accountData, null, 2) + ); + return identity; } @@ -210,6 +249,15 @@ async function register( if (response.status === 200 && response.data.success?.handleId) { // Return all success details + const registrationData = { + activeDid, + jwtToken, + response: response.data.success + }; + await writeFile( + join(GENERATED_DIR, 'registration.json'), + JSON.stringify(registrationData, null, 2) + ); return { success: true, handleId: response.data.success.handleId, @@ -225,11 +273,29 @@ async function register( // If we have success data but no handleId, it might be a partial success if (response.data.success) { + const registrationData = { + activeDid, + jwtToken, + response: response.data.success + }; + await writeFile( + join(GENERATED_DIR, 'registration.json'), + JSON.stringify(registrationData, null, 2) + ); return { success: true, ...response.data.success }; } else { + const registrationData = { + activeDid, + jwtToken, + response: response.data + }; + await writeFile( + join(GENERATED_DIR, 'registration.json'), + JSON.stringify(registrationData, null, 2) + ); return { error: "Registration failed", details: response.data }; } } catch (error: any) { @@ -247,6 +313,15 @@ async function register( } console.error(); + const registrationData = { + activeDid, + jwtToken, + response: { error: `Error submitting claim: ${error.message}` } + }; + await writeFile( + join(GENERATED_DIR, 'registration.json'), + JSON.stringify(registrationData, null, 2) + ); return { error: `Error submitting claim: ${error.message}` }; } } @@ -284,7 +359,24 @@ async function fetchClaim( } ); - return response.data; + const claimData = response.data; + + // Save claim data + try { + await writeFile( + join(GENERATED_DIR, 'claim_details.json'), + JSON.stringify({ + claim_id: claimId, + active_did: activeDid, + response: claimData + }, null, 2) + ); + } catch (err) { + console.error('Error saving claim data:', err); + } + + return claimData; + } catch (error: any) { console.error("\nError fetching claim:"); if (error.response) { @@ -293,14 +385,77 @@ async function fetchClaim( } else { console.error(error.message); } + // Save error state + try { + await writeFile( + join(GENERATED_DIR, 'claim_details.json'), + JSON.stringify({ + claim_id: claimId, + active_did: activeDid, + error: error.message || 'Unknown error', + response: error.response?.data + }, null, 2) + ); + } catch (writeErr) { + console.error('Error saving claim error:', writeErr); + } throw error; } } +/** + * Generate test environment data for deeplink testing + */ +async function generateTestEnv( + identity: IIdentifier, + jwtToken: string, + apiServer: string = API_SERVER +): Promise { + // Create test data structure + const testEnvData = { + CONTACT1_DID: identity.did, + CONTACT1_KEY: identity.keys[0].privateKeyHex, + CONTACT2_DID: `did:ethr:${Wallet.createRandom().address}`, // Generate random DID for contact 2 + CONTACT2_KEY: Wallet.createRandom().privateKey.substring(2), // Remove '0x' + ISSUER_DID: ENDORSER_DID, + ISSUER_KEY: ENDORSER_PRIVATE_KEY, + TEST_JWT: jwtToken, + API_SERVER: apiServer + }; + + // Write test environment variables + const envContent = Object.entries(testEnvData) + .map(([key, value]) => `export ${key}="${value}"`) + .join('\n'); + + await writeFile( + join(GENERATED_DIR, 'test-env.sh'), + envContent + '\n', + 'utf-8' + ); + + // Write test contacts data + const contactsData = { + contacts: [ + { did: testEnvData.CONTACT1_DID, name: 'Test Contact 1' }, + { did: testEnvData.CONTACT2_DID, name: 'Test Contact 2' } + ] + }; + + await writeFile( + join(GENERATED_DIR, 'contacts.json'), + JSON.stringify(contactsData, null, 2), + 'utf-8' + ); +} + /** * Main execution flow */ async function main() { + // Initialize directories first + await initializeDirectories(); + try { // Create a new DID const identity = await initializeAccount(); @@ -321,6 +476,9 @@ async function main() { ); console.log("\nClaim Details:"); console.log(JSON.stringify(claimDetails, null, 2)); + + // Generate test environment data + await generateTestEnv(identity, result.claimId); } } catch (error: any) { console.error("Error:", error.message); @@ -339,5 +497,6 @@ export { initializeAccount, createEndorserJwt, register, - fetchClaim + fetchClaim, + generateTestEnv }; \ No newline at end of file diff --git a/test-scripts/run-deeplink-tests.sh b/test-scripts/run-deeplink-tests.sh index 91de29f..b0cf048 100755 --- a/test-scripts/run-deeplink-tests.sh +++ b/test-scripts/run-deeplink-tests.sh @@ -1,175 +1,138 @@ #!/bin/bash +# File header documentation +# @file run-deeplink-tests.sh +# @brief Automated deeplink testing script using generated JSON files and ADB +# @author Matthew Raymer +# @date $(date +%Y-%m-%d) + +# Color definitions +BLUE='\033[0;34m' +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' + # Parse command line arguments -GENERATE_ONLY=false -ALL_TESTS=false -while getopts "ga" opt; do +TIMEOUT=5 +TEST_MODE="execute" # Default to execute mode + +while getopts "t:p" opt; do case $opt in - g) GENERATE_ONLY=true ;; - a) ALL_TESTS=true ;; + t) TIMEOUT=$OPTARG ;; + p) TEST_MODE="print" ;; \?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;; esac done -# Directory for storing generated DIDs -mkdir -p .generated +# Verify .generated directory exists +if [ ! -d .generated ]; then + echo -e "${RED}Error: .generated directory not found${NC}" + echo "Please run the DID generation script first" + exit 1 +fi -# Function to validate environment variables -validate_env() { - local missing=false - for var in CONTACT1_DID CONTACT1_KEY CONTACT2_DID CONTACT2_KEY ISSUER_DID ISSUER_KEY; do - if [ -z "${!var}" ]; then - echo "Error: $var is not set" >&2 - missing=true +# Function to read and validate JSON files +validate_json_files() { + local required_files=("test-env.sh" "claim_details.json" "contacts.json") + for file in "${required_files[@]}"; do + if [ ! -f ".generated/$file" ]; then + echo -e "${RED}Error: Missing required file .generated/$file${NC}" + return 1 fi done - if [ "$missing" = true ]; then - rm -f .generated/test-env.sh # Remove invalid env file - echo "Please regenerate DIDs by running:" >&2 - echo "./test-scripts/run-deeplink-tests.sh -g" >&2 - return 1 - fi return 0 } -# Check if we already have generated DIDs -if [ -f .generated/test-env.sh ] && [ "$GENERATE_ONLY" = false ]; then - echo "Using existing DIDs from .generated/test-env.sh" - source .generated/test-env.sh - validate_env || exit 1 -else - # Function to extract DID info from did_generator.sh output - extract_did_info() { - local output="$1" - - # Debug: Show what we received - echo "DEBUG: extract_did_info received: '$output'" >&2 - - # Parse and validate JSON - if ! printf '%s' "$output" | jq -e 'if type == "object" and .status == "success" then true else false end' >/dev/null 2>&1; then - echo "Error: DID generation failed" >&2 - echo "Error details:" >&2 - if ! printf '%s' "$output" | jq -e . >/dev/null 2>&1; then - echo "Invalid JSON output: $output" >&2 - else - printf '%s' "$output" | jq -r '"\(.stage): \(.error // "Unknown error")"' >&2 - fi +# Function to URL encode string +urlencode() { + local string="${1}" + local strlen=${#string} + local encoded="" + local pos c o + + for (( pos=0 ; pos&2 - - if [ $GEN_STATUS -ne 0 ]; then - echo "Error: did_generator.sh failed with status $GEN_STATUS" >&2 +# Main test sequence +main() { + # Validate environment and files + if ! validate_json_files; then exit 1 fi - CONTACT1_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') - echo "DEBUG: After tr command: '$CONTACT1_OUTPUT'" >&2 + # Source environment variables + source .generated/test-env.sh - # Debug: Show what we're passing to extract_did_info - echo "DEBUG: Calling extract_did_info with: '$CONTACT1_OUTPUT'" >&2 - CONTACT1_INFO=$(extract_did_info "$CONTACT1_OUTPUT") - EXTRACT_STATUS=$? - echo "DEBUG: extract_did_info returned status: $EXTRACT_STATUS" >&2 - echo "DEBUG: CONTACT1_INFO: '$CONTACT1_INFO'" >&2 + # Load JSON data + CLAIM_DETAILS=$(cat .generated/claim_details.json) + CONTACTS=$(cat .generated/contacts.json) - if [ $EXTRACT_STATUS -ne 0 ]; then - echo "DEBUG: extract_did_info failed" >&2 - exit 1 - fi - CONTACT1_DID=$(printf '%s' "$CONTACT1_INFO" | jq -r .did) - CONTACT1_KEY=$(printf '%s' "$CONTACT1_INFO" | jq -r .privateKey) - - echo "Generating second contact DID (Jordan)..." - DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh) - CONTACT2_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') - CONTACT2_INFO=$(extract_did_info "$CONTACT2_OUTPUT") - if [ $? -ne 0 ]; then - exit 1 - fi - CONTACT2_DID=$(printf '%s' "$CONTACT2_INFO" | jq -r .did) - CONTACT2_KEY=$(printf '%s' "$CONTACT2_INFO" | jq -r .privateKey) - - echo "Generating issuer DID..." - DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh) - ISSUER_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') - ISSUER_INFO=$(extract_did_info "$ISSUER_OUTPUT") - if [ $? -ne 0 ]; then - exit 1 - fi - ISSUER_DID=$(printf '%s' "$ISSUER_INFO" | jq -r .did) - ISSUER_KEY=$(printf '%s' "$ISSUER_INFO" | jq -r .privateKey) - - # Add some visual feedback about the generated DIDs - echo - echo "Generated DIDs:" - echo " Contact 1: ${CONTACT1_DID:0:20}..." - echo " Contact 2: ${CONTACT2_DID:0:20}..." - echo " Issuer: ${ISSUER_DID:0:20}..." - echo - - # Create a temporary env file with the generated DIDs - echo "Creating test-env.sh with generated DIDs..." - cat > .generated/test-env.sh << EOF -#!/bin/bash -# Generated $(date) -export CONTACT1_DID="$CONTACT1_DID" -export CONTACT1_KEY="$CONTACT1_KEY" -export CONTACT2_DID="$CONTACT2_DID" -export CONTACT2_KEY="$CONTACT2_KEY" -export ISSUER_DID="$ISSUER_DID" -export ISSUER_KEY="$ISSUER_KEY" -EOF + # 1. Claim-based deeplinks + execute_deeplink "timesafari://claim-cert/$(jq -r .claim_id <<< "$CLAIM_DETAILS")" \ + "Testing claim certificate view" + + execute_deeplink "timesafari://claim-add-raw/$(jq -r .claim_id <<< "$CLAIM_DETAILS")" \ + "Testing raw claim addition" - # Debug output - echo "Contents of generated test-env.sh:" - cat .generated/test-env.sh + # 2. DID-based deeplinks + execute_deeplink "timesafari://did/$CONTACT1_DID" \ + "Testing DID view" + + execute_deeplink "timesafari://contact-edit/$CONTACT1_DID" \ + "Testing contact editing" - # Make sure file is executable - chmod +x .generated/test-env.sh + # 3. JSON-based deeplinks + execute_deeplink "timesafari://contacts/import?contacts=$(jq -r @uri <<< "$CONTACTS")" \ + "Testing contacts import" - # Source and validate the newly created env file - echo "Sourcing generated test-env.sh..." - source .generated/test-env.sh - validate_env || exit 1 -fi + echo -e "\n${GREEN}All deeplink tests completed${NC}" +} -if [ "$GENERATE_ONLY" = true ]; then - echo "Generated DIDs and created environment file at .generated/test-env.sh" - echo "To use these DIDs, run:" - echo "source .generated/test-env.sh" - exit 0 +# Check for adb if not in print mode +if [ "$TEST_MODE" = "execute" ]; then + if ! command -v adb >/dev/null 2>&1; then + echo -e "${YELLOW}Warning: adb not found, switching to print mode${NC}" + TEST_MODE="print" + elif [ -z "$(adb devices | grep -v List | grep device)" ]; then + echo -e "${YELLOW}Warning: no devices/emulators found, switching to print mode${NC}" + TEST_MODE="print" + fi fi -# Create contacts JSON for deep links -CONTACTS_JSON=$(jq -n \ - --arg did1 "$CONTACT1_DID" \ - --arg did2 "$CONTACT2_DID" \ - '[ - {"did": $did1}, - { - "did": $did2, - "name": "Jordan", - "nextPubKeyHashB64": "IBfRZfwdzeKOzqCx8b+WlLpMJHOAT9ZknIDJo7F3rZE=", - "publicKeyBase64": "A1eIndfaxgMpVwyD5dYe74DgjuIo5SwPZFCcLdOemjf" - } - ]') - -export CONTACTS_JSON -export TEST_MODE=${TEST_MODE:-print} # Use TEST_MODE from environment or default to print - -# Run deep link tests in order -if [ "$ALL_TESTS" = true ]; then - ./test-scripts/test-deeplinks.sh -t 5 -a -else - ./test-scripts/test-deeplinks.sh -t 5 -fi \ No newline at end of file +# Execute main test sequence +main \ No newline at end of file