Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Integration Tests Enhancement and Coinbase Commerce Integration #1767

Merged
merged 18 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 6 additions & 9 deletions .github/workflows/integrationTests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches:
- "*"
pull_request_target:
pull_request:
branches:
- "*"

Expand Down Expand Up @@ -33,12 +33,9 @@ jobs:
- name: Build packages
run: pnpm build

- name: Check for API key
run: |
if [ -z "$OPENAI_API_KEY" ]; then
echo "Error: OPENAI_API_KEY is not set."
exit 1
fi

- name: Run integration tests
run: pnpm run integrationTests
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COINBASE_COMMERCE_KEY: ${{ secrets.COINBASE_COMMERCE_KEY }}
run: |
pnpm run integrationTests
18 changes: 11 additions & 7 deletions packages/plugin-coinbase/src/plugins/commerce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export async function getAllCharges(apiKey: string) {
// Function to fetch details of a specific charge
export async function getChargeDetails(apiKey: string, chargeId: string) {
elizaLogger.debug("Starting getChargeDetails function");
const getUrl = `${url}${chargeId}`;
const getUrl = `${url}/${chargeId}`;

try {
const response = await fetch(getUrl, {
Expand Down Expand Up @@ -204,8 +204,8 @@ export const createCoinbaseChargeAction: Action = {
text: `Charge created successfully: ${chargeResponse.hosted_url}`,
attachments: [
{
id: crypto.randomUUID(),
url: chargeResponse.id,
id: chargeResponse.id,
url: chargeResponse.hosted_url,
title: "Coinbase Commerce Charge",
description: `Charge ID: ${chargeResponse.id}`,
text: `Pay here: ${chargeResponse.hosted_url}`,
Expand Down Expand Up @@ -351,6 +351,7 @@ export const getAllChargesAction: Action = {
callback(
{
text: `Successfully fetched all charges. Total charges: ${charges.length}`,
attachments: charges,
},
[]
);
Expand Down Expand Up @@ -439,17 +440,20 @@ export const getChargeDetailsAction: Action = {

elizaLogger.info("Fetched charge details:", chargeDetails);

const chargeData = chargeDetails.data;

callback(
{
text: `Successfully fetched charge details for ID: ${charge.id}`,
attachments: [
{
id: crypto.randomUUID(),
url: chargeDetails.hosted_url,
id: chargeData.id,
url: chargeData.hosted_url,
title: `Charge Details for ${charge.id}`,
description: `Details: ${JSON.stringify(chargeDetails, null, 2)}`,
source: "coinbase",
text: "",
description: JSON.stringify(chargeDetails, null, 2),
text: `Pay here: ${chargeData.hosted_url}`,
contentType: "application/json",
},
],
},
Expand Down
149 changes: 145 additions & 4 deletions tests/test1.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,155 @@ import { send, log, logError, runIntegrationTest } from "./testLibrary.mjs";

async function helloTrump() {
const reply = await send("Hi");
assert(reply.length > 10);
assert(reply.length > 0, "Response should not be empty");
const response = reply[0];
assert(response.text, "Response should have text property");
assert(
response.text.length > 10,
`Response should be longer than 10 characters, is ${reply.length}`
);
}
helloTrump.description = "Hello Trump";
helloTrump.skipIf = !process.env.OPENAI_API_KEY;

async function coinbaseTest() {
// TODO
async function coinbaseCommerceChargeTest() {
const chargeDescription = "Exclusive digital artwork collection";
const chargeRequest = `Create a charge for $100 USD for Digital Art NFT with description '${chargeDescription}'`;
const response = await send(chargeRequest);

// Verify response structure
assert(Array.isArray(response), "Response should be an array");
assert(response.length === 2, "Response should contain two messages");

// Verify initial response
const initialResponse = response[0];
assert.strictEqual(initialResponse.action, "CREATE_CHARGE");

// Verify charge creation response
const chargeResponse = response[1];
assert(
chargeResponse.text.startsWith("Charge created successfully:"),
"Should indicate successful charge creation"
);
assert(
chargeResponse.text.includes("https://commerce.coinbase.com/pay/"),
"Should contain valid Coinbase Commerce URL"
);

// Verify attachment structure
assert(
Array.isArray(chargeResponse.attachments),
"Should have attachments array"
);
assert(
chargeResponse.attachments.length === 1,
"Should have one attachment"
);

const attachment = chargeResponse.attachments[0];
assert.strictEqual(attachment.source, "coinbase");
assert.strictEqual(attachment.title, "Coinbase Commerce Charge");
assert(attachment.id, "Should have an ID");
assert(attachment.url, "Should have a charge ID URL");
assert(
attachment.description.startsWith("Charge ID:"),
"Should have charge ID description"
);
assert(attachment.text.startsWith("Pay here:"), "Should have payment URL");
assert(
attachment.text.includes("https://commerce.coinbase.com/pay/"),
"Should have valid Coinbase Commerce URL"
);

// Store the created charge ID for later comparison
const createdChargeId = attachment.id;
const createdChargeUrl = attachment.url;

// Fetch and verify all charges
const chargesResponse = await send("Fetch all charges");

// Verify response structure
assert(
Array.isArray(chargesResponse),
"Charges response should be an array"
);
assert(
chargesResponse.length === 2,
"Should have two messages (prompt and response)"
);

// Verify charges data
const charges = chargesResponse[1].attachments;
assert(Array.isArray(charges), "Charges should be an array");
assert(charges.length > 0, "Should have at least one charge");

// Verify each charge has required properties
charges.forEach((charge) => {
assert(charge.id, "Each charge should have an id");
assert(charge.hosted_url, "Each charge should have a hosted_url");
assert(
charge.hosted_url.includes("commerce.coinbase.com/pay/"),
"hosted_url should be a valid Coinbase URL"
);
assert(charge.web3_data, "Each charge should have web3_data object");
});

// Verify the previously created charge exists in the list
const foundCharge = charges.find((charge) => charge.id === createdChargeId);
assert(foundCharge, "Previously created charge should exist in the list");
assert.strictEqual(
foundCharge.hosted_url,
createdChargeUrl,
"Hosted URL should match"
);
assert.strictEqual(
foundCharge.description,
chargeDescription,
"Description should match"
);

// Test GetChargeDetails action
const getDetailsResponse = await send(
`Get details for charge ID: ${createdChargeId}`
);

// Verify response structure for charge details
assert(
Array.isArray(getDetailsResponse),
"GetChargeDetails response should be an array"
);
assert(
getDetailsResponse.length === 2,
"Should have two messages (prompt and response)"
);

// Verify charge details response
const detailsResponse = getDetailsResponse[1];
assert(
Array.isArray(detailsResponse.attachments),
"Should have attachments array"
);

const detailsAttachment = detailsResponse.attachments[0];

const chargeData = JSON.parse(detailsAttachment.description);

assert.equal(
chargeData.data.hosted_url,
createdChargeUrl,
"Hosted URLs should match"
);
assert.equal(
chargeData.data.description,
chargeDescription,
"Charge description should match"
);
}
coinbaseCommerceChargeTest.description = "Coinbase Commerce Charge";
coinbaseCommerceChargeTest.skipIf =
!process.env.OPENAI_API_KEY || !process.env.COINBASE_COMMERCE_KEY;

const testSuite = [helloTrump]; // Add tests here
const testSuite = [helloTrump, coinbaseCommerceChargeTest];
try {
for (const test of testSuite) await runIntegrationTest(test);
} catch (error) {
Expand Down
45 changes: 35 additions & 10 deletions tests/testLibrary.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const DEFAULT_AGENT_ID = stringToUuid(DEFAULT_CHARACTER ?? uuidv4());

function projectRoot() {
return path.join(import.meta.dirname, "..");
// return "/Users/piotr/Documents/GitHub/Sifchain/eliza"
}

function log(message) {
Expand Down Expand Up @@ -109,7 +110,7 @@ async function sendPostRequest(url, method, payload) {
if (!response.ok)
throw new Error(`HTTP error! status: ${response.status}`);
const data = await response.json();
return data[0].text;
return data;
} catch (error) {
throw new Error(`Failed to send message: ${error.message}`);
}
Expand All @@ -125,15 +126,38 @@ async function send(message) {
}

async function runIntegrationTest(fn) {
const proc = await startAgent();
try {
await fn();
log("✓ Test passed");
} catch (error) {
log("✗ Test failed");
logError(error);
} finally {
await stopAgent(proc);
log(fn);
const skip = fn.hasOwnProperty("skipIf") ? fn.skipIf : false;
if (skip) {
log(
fn.description
? `Skipping test ${fn.description}...`
: "Skipping test..."
);
} else {
log(
fn.description
? `Running test ${fn.description}...`
: "Running test..."
);
const proc = await startAgent();
try {
await fn();
log(
fn.description
? `✓ Test ${fn.description} passed`
: "✓ Test passed"
);
} catch (error) {
log(
fn.description
? `✗ Test ${fn.description} failed`
: "✗ Test failed"
);
logError(error);
} finally {
await stopAgent(proc);
}
}
}

Expand All @@ -149,4 +173,5 @@ export {
runIntegrationTest,
log,
logError,
sleep,
};
Loading