Skip to content

Commit 899b92c

Browse files
committed
initial commit
1 parent ec8f91c commit 899b92c

File tree

13 files changed

+1526
-0
lines changed

13 files changed

+1526
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.serverless
2+
.env
3+
node_modules

EcommerceClient.js

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
class EcommerceClient {
2+
constructor(config = {}) {
3+
this.baseURL = config.baseURL || 'https://your-api-endpoint.amazonaws.com';
4+
this.timeout = config.timeout || 5000;
5+
this.headers = {
6+
'Content-Type': 'application/json',
7+
...(config.headers || {})
8+
};
9+
}
10+
11+
// Helper method to handle API calls
12+
async fetchWithTimeout(endpoint, options = {}) {
13+
const controller = new AbortController();
14+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
15+
16+
try {
17+
const response = await fetch(`${this.baseURL}${endpoint}`, {
18+
...options,
19+
headers: this.headers,
20+
signal: controller.signal
21+
});
22+
23+
const responseData = await response.json().catch(() => ({}));
24+
25+
if (!response.ok) {
26+
throw new Error(`API Error: ${response.status} - ${responseData.error || response.statusText || 'Unknown error'}
27+
\nEndpoint: ${endpoint}
28+
\nResponse: ${JSON.stringify(responseData)}`);
29+
}
30+
31+
return responseData.data;
32+
} finally {
33+
clearTimeout(timeoutId);
34+
}
35+
}
36+
37+
// Users
38+
async getAllUsers() {
39+
return this.fetchWithTimeout('/users', {
40+
method: 'GET'
41+
});
42+
}
43+
44+
// Sales
45+
async getSales() {
46+
return this.fetchWithTimeout('/sales', {
47+
method: 'GET'
48+
});
49+
}
50+
51+
// Inventory
52+
async getInventory() {
53+
return this.fetchWithTimeout('/inventory', {
54+
method: 'GET'
55+
});
56+
}
57+
58+
async updateInventory(productId, warehouseId, quantity) {
59+
return this.fetchWithTimeout('/inventory', {
60+
method: 'PUT',
61+
body: JSON.stringify({
62+
product_id: productId,
63+
warehouse_id: warehouseId,
64+
quantity
65+
})
66+
});
67+
}
68+
69+
// Fulfillment
70+
async createFulfillment(orderId, warehouseId, items) {
71+
return this.fetchWithTimeout('/fulfillment', {
72+
method: 'POST',
73+
body: JSON.stringify({
74+
order_id: orderId,
75+
warehouse_id: warehouseId,
76+
items
77+
})
78+
});
79+
}
80+
81+
async getFulfillmentStatus(fulfillmentId) {
82+
return this.fetchWithTimeout(`/fulfillment/${fulfillmentId}`, {
83+
method: 'GET'
84+
});
85+
}
86+
}
87+
88+
// Example usage
89+
const runExample = async () => {
90+
const client = new EcommerceClient({
91+
baseURL: 'https://your-api-endpoint.amazonaws.com',
92+
timeout: 5000,
93+
headers: {
94+
'Authorization': 'Bearer your-token-here'
95+
}
96+
});
97+
98+
try {
99+
// Get all users
100+
const users = await client.getAllUsers();
101+
console.log('Users:', users);
102+
103+
// Get sales data
104+
const sales = await client.getSales();
105+
console.log('Sales:', sales);
106+
107+
// Check inventory
108+
const inventory = await client.getInventory();
109+
console.log('Inventory:', inventory);
110+
111+
// Update inventory
112+
const updatedInventory = await client.updateInventory(1, 1, 10);
113+
console.log('Updated Inventory:', updatedInventory);
114+
115+
// Create a fulfillment
116+
const fulfillment = await client.createFulfillment('ORD-2024-009', 1, [
117+
{ product_id: 1, quantity: 1 },
118+
{ product_id: 2, quantity: 2 }
119+
]);
120+
console.log('Created Fulfillment:', fulfillment);
121+
122+
// Check fulfillment status
123+
const status = await client.getFulfillmentStatus(fulfillment.id);
124+
console.log('Fulfillment Status:', status);
125+
126+
} catch (error) {
127+
console.error('Error:', error.message);
128+
}
129+
};
130+
131+
// Test with fake data
132+
const testClient = async () => {
133+
const client = new EcommerceClient({
134+
baseURL: 'https://fake-api.example.com'
135+
});
136+
137+
// Mock response for testing
138+
globalThis.fetch = async (url, options) => {
139+
const mockData = {
140+
data: {
141+
id: 1,
142+
status: 'success',
143+
timestamp: new Date().toISOString()
144+
}
145+
};
146+
147+
return {
148+
ok: true,
149+
json: async () => mockData
150+
};
151+
};
152+
153+
try {
154+
const result = await client.getSales();
155+
console.log('Test result:', result);
156+
} catch (error) {
157+
console.error('Test error:', error);
158+
}
159+
};
160+
161+
module.exports = EcommerceClient;

app.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import EcommerceClient from './EcommerceClient.js';
2+
3+
const client = new EcommerceClient({
4+
baseURL: 'https://q25v3hkkfk.execute-api.us-east-1.amazonaws.com',
5+
timeout: 5000,
6+
7+
});
8+
9+
// Example call
10+
const inventory = await client.getInventory();
11+
console.log(inventory);

load-testing.js

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
const EcommerceClient = require('./EcommerceClient');
2+
3+
// Utility to add random delay
4+
const randomDelay = (min, max) => {
5+
const delay = Math.floor(Math.random() * (max - min + 1) + min);
6+
return new Promise(resolve => setTimeout(resolve, delay));
7+
};
8+
9+
// Simulate a single user's behavior
10+
class VirtualUser {
11+
constructor(userId, baseURL) {
12+
this.userId = userId;
13+
this.client = new EcommerceClient({
14+
baseURL,
15+
headers: {
16+
'User-Agent': `Virtual-User-${userId}`,
17+
'X-Test-User-Id': userId.toString()
18+
}
19+
});
20+
}
21+
22+
async randomAction() {
23+
// List of possible actions with their relative weights
24+
const actions = [
25+
{ weight: 40, action: () => this.client.getInventory() }, // Most common
26+
{ weight: 30, action: () => this.client.getSales() }, // Very common
27+
{ weight: 15, action: () => this.client.getFulfillmentStatus(1) }, // Less common
28+
{ weight: 10, action: () => this.client.getAllUsers() }, // Occasional
29+
{ weight: 5, action: () => this.createRandomFulfillment() } // Rare
30+
];
31+
32+
// Choose random action based on weights
33+
const totalWeight = actions.reduce((sum, action) => sum + action.weight, 0);
34+
let random = Math.random() * totalWeight;
35+
36+
for (const { weight, action } of actions) {
37+
if (random < weight) return action();
38+
random -= weight;
39+
}
40+
}
41+
42+
async createRandomFulfillment() {
43+
const orderId = `ORD-${Date.now()}-${this.userId}`;
44+
const warehouseId = Math.floor(Math.random() * 3) + 1;
45+
const items = [
46+
{
47+
product_id: Math.floor(Math.random() * 5) + 1,
48+
quantity: Math.floor(Math.random() * 3) + 1
49+
}
50+
];
51+
52+
return this.client.createFulfillment(orderId, warehouseId, items);
53+
}
54+
55+
async simulateUserSession(duration = 60000) {
56+
const startTime = Date.now();
57+
const results = [];
58+
59+
while (Date.now() - startTime < duration) {
60+
try {
61+
const startActionTime = Date.now();
62+
const result = await this.randomAction();
63+
const endActionTime = Date.now();
64+
65+
results.push({
66+
userId: this.userId,
67+
success: true,
68+
responseTime: endActionTime - startActionTime
69+
});
70+
71+
// Random delay between actions (0.5 to 3 seconds)
72+
await randomDelay(500, 3000);
73+
} catch (error) {
74+
results.push({
75+
userId: this.userId,
76+
success: false,
77+
error: error.message
78+
});
79+
}
80+
}
81+
82+
return results;
83+
}
84+
}
85+
86+
// Load test orchestrator
87+
class LoadTest {
88+
constructor(config = {}) {
89+
this.baseURL = config.baseURL || 'https://your-api-endpoint.amazonaws.com';
90+
this.numUsers = config.numUsers || 10;
91+
this.testDuration = config.testDuration || 60000; // 1 minute default
92+
this.results = [];
93+
}
94+
95+
async run() {
96+
console.log(`Starting load test with ${this.numUsers} users for ${this.testDuration/1000} seconds`);
97+
const startTime = Date.now();
98+
99+
// Create and start all virtual users
100+
const users = Array.from({ length: this.numUsers },
101+
(_, i) => new VirtualUser(i + 1, this.baseURL)
102+
);
103+
104+
const userSessions = users.map(user =>
105+
user.simulateUserSession(this.testDuration)
106+
);
107+
108+
// Wait for all user sessions to complete
109+
const allResults = await Promise.all(userSessions);
110+
this.results = allResults.flat();
111+
112+
// Calculate statistics
113+
this.printStatistics();
114+
}
115+
116+
printStatistics() {
117+
const totalRequests = this.results.length;
118+
const successfulRequests = this.results.filter(r => r.success).length;
119+
const failedRequests = totalRequests - successfulRequests;
120+
121+
const responseTimes = this.results
122+
.filter(r => r.success)
123+
.map(r => r.responseTime);
124+
125+
const avgResponseTime = responseTimes.reduce((a, b) => a + b, 0) / responseTimes.length;
126+
127+
console.log('\nLoad Test Results:');
128+
console.log('==================');
129+
console.log(`Total Requests: ${totalRequests}`);
130+
console.log(`Successful Requests: ${successfulRequests}`);
131+
console.log(`Failed Requests: ${failedRequests}`);
132+
console.log(`Success Rate: ${((successfulRequests/totalRequests) * 100).toFixed(2)}%`);
133+
console.log(`Average Response Time: ${avgResponseTime.toFixed(2)}ms`);
134+
135+
// Print error distribution if any failures
136+
if (failedRequests > 0) {
137+
const errorCounts = this.results
138+
.filter(r => !r.success)
139+
.reduce((acc, curr) => {
140+
acc[curr.error] = (acc[curr.error] || 0) + 1;
141+
return acc;
142+
}, {});
143+
144+
console.log('\nError Distribution:');
145+
Object.entries(errorCounts).forEach(([error, count]) => {
146+
console.log(`${error}: ${count} occurrences`);
147+
});
148+
}
149+
}
150+
}
151+
152+
// Example usage
153+
const runLoadTest = async () => {
154+
const loadTest = new LoadTest({
155+
baseURL: 'https://q25v3hkkfk.execute-api.us-east-1.amazonaws.com',
156+
numUsers: 50, // 50 concurrent users
157+
testDuration: 300000 // 5 minutes
158+
});
159+
160+
await loadTest.run();
161+
};
162+
163+
// Run the test
164+
runLoadTest().catch(console.error);

neon-lambda/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
.serverless

0 commit comments

Comments
 (0)