forked from trent_larson/crowd-funder-for-time-pwa
feat: add JSON output files and improve test flow
- Add .generated directory for test artifacts - Save key derivation data to key_derivation.json - Save account initialization to account_init.json - Save registration data to registration.json - Save claim details to claim_details.json - Save test environment to test-env.sh - Save contacts data to contacts.json - Add proper error handling for file operations - Improve deeplink test flow with JSON-based data - Add color output and better status messages - Add ADB device detection and fallback to print mode Technical Changes: - Add file system operations with proper error handling - Standardize JSON output format across Python/TypeScript - Update test flow to use generated JSON files - Add proper typing for registration response - Improve error reporting and debug output This improves the test workflow by saving all intermediate data as JSON files that can be used by other test scripts. The deeplink testing now uses this data instead of environment variables for better reliability.
This commit is contained in:
@@ -54,6 +54,7 @@ from jwcrypto import jwk
|
|||||||
import asyncio
|
import asyncio
|
||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'" # Custom derivation path for TimeSafari
|
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_DID = "did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F" # Keep original case
|
||||||
ENDORSER_PRIVATE_KEY = "2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b"
|
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(
|
def derive_address(
|
||||||
mnemonic: str,
|
mnemonic: str,
|
||||||
@@ -98,6 +109,21 @@ def derive_address(
|
|||||||
print(f" Private Key: {private_hex[:8]}...")
|
print(f" Private Key: {private_hex[:8]}...")
|
||||||
print(f" Public Key: {public_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
|
return address, private_hex, public_hex, derivation_path
|
||||||
|
|
||||||
def new_identifier(
|
def new_identifier(
|
||||||
@@ -165,13 +191,16 @@ def initialize_account() -> Dict[str, Any]:
|
|||||||
# Create DID identifier
|
# Create DID identifier
|
||||||
identity = new_identifier(address, public_hex, private_hex, derivation_path)
|
identity = new_identifier(address, public_hex, private_hex, derivation_path)
|
||||||
|
|
||||||
# Format account data
|
# Save account data
|
||||||
account_data = {
|
account_data = {
|
||||||
"did": identity["did"],
|
"did": identity["did"],
|
||||||
"identity": identity,
|
"identity": identity,
|
||||||
"mnemonic": mnemonic,
|
"mnemonic": mnemonic,
|
||||||
"derivation_path": derivation_path
|
"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("\nAccount initialized:")
|
||||||
print(json.dumps(account_data, indent=2))
|
print(json.dumps(account_data, indent=2))
|
||||||
print()
|
print()
|
||||||
@@ -328,6 +357,15 @@ async def register(
|
|||||||
response_data = response.json()
|
response_data = response.json()
|
||||||
|
|
||||||
if response.status_code == 200 and response_data.get("success", {}).get("handleId"):
|
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 {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"handleId": response_data["success"]["handleId"],
|
"handleId": response_data["success"]["handleId"],
|
||||||
@@ -393,15 +431,69 @@ async def fetch_claim(
|
|||||||
headers={
|
headers={
|
||||||
'Authorization': f'Bearer {auth_token}',
|
'Authorization': f'Bearer {auth_token}',
|
||||||
'Content-Type': 'application/json'
|
'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:
|
except requests.RequestException as e:
|
||||||
print("\nError fetching claim:")
|
print("\nError fetching claim:")
|
||||||
print(f"Error: {str(e)}")
|
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
|
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
|
# Main execution
|
||||||
async def main():
|
async def main():
|
||||||
"""
|
"""
|
||||||
@@ -442,28 +534,38 @@ async def main():
|
|||||||
Registration result: {"success": true}
|
Registration result: {"success": true}
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
# Step 1: Create a new DID
|
# Initialize directories first
|
||||||
identity = initialize_account()
|
initialize_directories()
|
||||||
active_did = identity["did"]
|
|
||||||
private_key_hex = identity["keys"][0]["privateKeyHex"]
|
|
||||||
|
|
||||||
# Step 2: Register the DID
|
try:
|
||||||
result = await register(active_did, private_key_hex)
|
# Step 1: Create a new DID
|
||||||
print("Registration result:", result)
|
identity = initialize_account()
|
||||||
|
active_did = identity["did"]
|
||||||
|
private_key_hex = identity["keys"][0]["privateKeyHex"]
|
||||||
|
|
||||||
# Step 3: If registration successful, fetch claim details
|
# Step 2: Register the DID
|
||||||
if result.get("success") and result.get("claimId"):
|
result = await register(active_did, private_key_hex)
|
||||||
print("\nFetching claim details...")
|
print("Registration result:", result)
|
||||||
try:
|
|
||||||
claim_details = await fetch_claim(
|
# Step 3: If registration successful, fetch claim details
|
||||||
result["claimId"],
|
if result.get("success") and result.get("claimId"):
|
||||||
active_did,
|
print("\nFetching claim details...")
|
||||||
private_key_hex
|
try:
|
||||||
)
|
claim_details = await fetch_claim(
|
||||||
print("\nClaim Details:")
|
result["claimId"],
|
||||||
print(json.dumps(claim_details, indent=2))
|
active_did,
|
||||||
except Exception as e:
|
private_key_hex
|
||||||
print(f"Error fetching claim: {str(e)}")
|
)
|
||||||
|
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__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import { HDNode } from '@ethersproject/hdnode';
|
|||||||
import { Wallet } from '@ethersproject/wallet';
|
import { Wallet } from '@ethersproject/wallet';
|
||||||
import { createJWT, ES256KSigner, SimpleSigner } from 'did-jwt';
|
import { createJWT, ES256KSigner, SimpleSigner } from 'did-jwt';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
import { mkdir, writeFile } from 'fs/promises';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'";
|
const DEFAULT_ROOT_DERIVATION_PATH = "m/84737769'/0'/0'/0'";
|
||||||
@@ -32,6 +34,19 @@ interface IIdentifier {
|
|||||||
services: any[];
|
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
|
* Generate a new mnemonic seed phrase
|
||||||
*/
|
*/
|
||||||
@@ -62,6 +77,24 @@ async function deriveAddress(
|
|||||||
console.log(` Private Key: ${privateHex.substring(0,8)}...`);
|
console.log(` Private Key: ${privateHex.substring(0,8)}...`);
|
||||||
console.log(` Public Key: ${publicHex.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];
|
return [address, privateHex, publicHex, derivationPath];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,6 +144,12 @@ async function initializeAccount(): Promise<IIdentifier> {
|
|||||||
console.log(JSON.stringify(accountData, null, 2));
|
console.log(JSON.stringify(accountData, null, 2));
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
|
// Save account data
|
||||||
|
await writeFile(
|
||||||
|
join(GENERATED_DIR, 'account_init.json'),
|
||||||
|
JSON.stringify(accountData, null, 2)
|
||||||
|
);
|
||||||
|
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +249,15 @@ async function register(
|
|||||||
|
|
||||||
if (response.status === 200 && response.data.success?.handleId) {
|
if (response.status === 200 && response.data.success?.handleId) {
|
||||||
// Return all success details
|
// 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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
handleId: response.data.success.handleId,
|
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 we have success data but no handleId, it might be a partial success
|
||||||
if (response.data.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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
...response.data.success
|
...response.data.success
|
||||||
};
|
};
|
||||||
} else {
|
} 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 };
|
return { error: "Registration failed", details: response.data };
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
@@ -247,6 +313,15 @@ async function register(
|
|||||||
}
|
}
|
||||||
console.error();
|
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}` };
|
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) {
|
} catch (error: any) {
|
||||||
console.error("\nError fetching claim:");
|
console.error("\nError fetching claim:");
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
@@ -293,14 +385,77 @@ async function fetchClaim(
|
|||||||
} else {
|
} else {
|
||||||
console.error(error.message);
|
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;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate test environment data for deeplink testing
|
||||||
|
*/
|
||||||
|
async function generateTestEnv(
|
||||||
|
identity: IIdentifier,
|
||||||
|
jwtToken: string,
|
||||||
|
apiServer: string = API_SERVER
|
||||||
|
): Promise<void> {
|
||||||
|
// 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
|
* Main execution flow
|
||||||
*/
|
*/
|
||||||
async function main() {
|
async function main() {
|
||||||
|
// Initialize directories first
|
||||||
|
await initializeDirectories();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a new DID
|
// Create a new DID
|
||||||
const identity = await initializeAccount();
|
const identity = await initializeAccount();
|
||||||
@@ -321,6 +476,9 @@ async function main() {
|
|||||||
);
|
);
|
||||||
console.log("\nClaim Details:");
|
console.log("\nClaim Details:");
|
||||||
console.log(JSON.stringify(claimDetails, null, 2));
|
console.log(JSON.stringify(claimDetails, null, 2));
|
||||||
|
|
||||||
|
// Generate test environment data
|
||||||
|
await generateTestEnv(identity, result.claimId);
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Error:", error.message);
|
console.error("Error:", error.message);
|
||||||
@@ -339,5 +497,6 @@ export {
|
|||||||
initializeAccount,
|
initializeAccount,
|
||||||
createEndorserJwt,
|
createEndorserJwt,
|
||||||
register,
|
register,
|
||||||
fetchClaim
|
fetchClaim,
|
||||||
|
generateTestEnv
|
||||||
};
|
};
|
||||||
@@ -1,175 +1,138 @@
|
|||||||
#!/bin/bash
|
#!/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
|
# Parse command line arguments
|
||||||
GENERATE_ONLY=false
|
TIMEOUT=5
|
||||||
ALL_TESTS=false
|
TEST_MODE="execute" # Default to execute mode
|
||||||
while getopts "ga" opt; do
|
|
||||||
|
while getopts "t:p" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
g) GENERATE_ONLY=true ;;
|
t) TIMEOUT=$OPTARG ;;
|
||||||
a) ALL_TESTS=true ;;
|
p) TEST_MODE="print" ;;
|
||||||
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
|
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Directory for storing generated DIDs
|
# Verify .generated directory exists
|
||||||
mkdir -p .generated
|
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
|
# Function to read and validate JSON files
|
||||||
validate_env() {
|
validate_json_files() {
|
||||||
local missing=false
|
local required_files=("test-env.sh" "claim_details.json" "contacts.json")
|
||||||
for var in CONTACT1_DID CONTACT1_KEY CONTACT2_DID CONTACT2_KEY ISSUER_DID ISSUER_KEY; do
|
for file in "${required_files[@]}"; do
|
||||||
if [ -z "${!var}" ]; then
|
if [ ! -f ".generated/$file" ]; then
|
||||||
echo "Error: $var is not set" >&2
|
echo -e "${RED}Error: Missing required file .generated/$file${NC}"
|
||||||
missing=true
|
return 1
|
||||||
fi
|
fi
|
||||||
done
|
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
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check if we already have generated DIDs
|
# Function to URL encode string
|
||||||
if [ -f .generated/test-env.sh ] && [ "$GENERATE_ONLY" = false ]; then
|
urlencode() {
|
||||||
echo "Using existing DIDs from .generated/test-env.sh"
|
local string="${1}"
|
||||||
source .generated/test-env.sh
|
local strlen=${#string}
|
||||||
validate_env || exit 1
|
local encoded=""
|
||||||
else
|
local pos c o
|
||||||
# Function to extract DID info from did_generator.sh output
|
|
||||||
extract_did_info() {
|
for (( pos=0 ; pos<strlen ; pos++ )); do
|
||||||
local output="$1"
|
c=${string:$pos:1}
|
||||||
|
case "$c" in
|
||||||
# Debug: Show what we received
|
[-_.~a-zA-Z0-9] ) o="${c}" ;;
|
||||||
echo "DEBUG: extract_did_info received: '$output'" >&2
|
* ) printf -v o '%%%02x' "'$c"
|
||||||
|
esac
|
||||||
# Parse and validate JSON
|
encoded+="${o}"
|
||||||
if ! printf '%s' "$output" | jq -e 'if type == "object" and .status == "success" then true else false end' >/dev/null 2>&1; then
|
done
|
||||||
echo "Error: DID generation failed" >&2
|
echo "${encoded}"
|
||||||
echo "Error details:" >&2
|
}
|
||||||
if ! printf '%s' "$output" | jq -e . >/dev/null 2>&1; then
|
|
||||||
echo "Invalid JSON output: $output" >&2
|
# Function to execute or print deeplink
|
||||||
else
|
execute_deeplink() {
|
||||||
printf '%s' "$output" | jq -r '"\(.stage): \(.error // "Unknown error")"' >&2
|
local url="$1"
|
||||||
fi
|
local description="$2"
|
||||||
|
local encoded_url=$(urlencode "$url")
|
||||||
|
|
||||||
|
echo -e "\n${BLUE}Testing: $description${NC}"
|
||||||
|
|
||||||
|
if [ "$TEST_MODE" = "print" ]; then
|
||||||
|
echo -e "${YELLOW}Deep Link URL:${NC}"
|
||||||
|
echo "$url"
|
||||||
|
echo -e "${YELLOW}Encoded URL:${NC}"
|
||||||
|
echo "$encoded_url"
|
||||||
|
echo "---"
|
||||||
|
else
|
||||||
|
echo "URL: $url"
|
||||||
|
if adb shell am start -a android.intent.action.VIEW -d "$encoded_url"; then
|
||||||
|
echo -e "${GREEN}Success: Deeplink executed${NC}"
|
||||||
|
sleep "$TIMEOUT"
|
||||||
|
else
|
||||||
|
echo -e "${RED}Error: Failed to execute deeplink${NC}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
# Return the successful JSON
|
}
|
||||||
printf '%s' "$output"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Generating first contact DID..."
|
# Main test sequence
|
||||||
# Debug: Show raw command output
|
main() {
|
||||||
DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh)
|
# Validate environment and files
|
||||||
GEN_STATUS=$?
|
if ! validate_json_files; then
|
||||||
echo "DEBUG: Raw did_generator.sh output: '$DEBUG_OUTPUT'" >&2
|
|
||||||
|
|
||||||
if [ $GEN_STATUS -ne 0 ]; then
|
|
||||||
echo "Error: did_generator.sh failed with status $GEN_STATUS" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CONTACT1_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r')
|
# Source environment variables
|
||||||
echo "DEBUG: After tr command: '$CONTACT1_OUTPUT'" >&2
|
|
||||||
|
|
||||||
# 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
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
# Debug output
|
|
||||||
echo "Contents of generated test-env.sh:"
|
|
||||||
cat .generated/test-env.sh
|
|
||||||
|
|
||||||
# Make sure file is executable
|
|
||||||
chmod +x .generated/test-env.sh
|
|
||||||
|
|
||||||
# Source and validate the newly created env file
|
|
||||||
echo "Sourcing generated test-env.sh..."
|
|
||||||
source .generated/test-env.sh
|
source .generated/test-env.sh
|
||||||
validate_env || exit 1
|
|
||||||
|
# Load JSON data
|
||||||
|
CLAIM_DETAILS=$(cat .generated/claim_details.json)
|
||||||
|
CONTACTS=$(cat .generated/contacts.json)
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
|
# 2. DID-based deeplinks
|
||||||
|
execute_deeplink "timesafari://did/$CONTACT1_DID" \
|
||||||
|
"Testing DID view"
|
||||||
|
|
||||||
|
execute_deeplink "timesafari://contact-edit/$CONTACT1_DID" \
|
||||||
|
"Testing contact editing"
|
||||||
|
|
||||||
|
# 3. JSON-based deeplinks
|
||||||
|
execute_deeplink "timesafari://contacts/import?contacts=$(jq -r @uri <<< "$CONTACTS")" \
|
||||||
|
"Testing contacts import"
|
||||||
|
|
||||||
|
echo -e "\n${GREEN}All deeplink tests completed${NC}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
fi
|
||||||
|
|
||||||
if [ "$GENERATE_ONLY" = true ]; then
|
# Execute main test sequence
|
||||||
echo "Generated DIDs and created environment file at .generated/test-env.sh"
|
main
|
||||||
echo "To use these DIDs, run:"
|
|
||||||
echo "source .generated/test-env.sh"
|
|
||||||
exit 0
|
|
||||||
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
|
|
||||||
Reference in New Issue
Block a user