Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

load-test.js

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

load-test.jsBlame126 lines · 1 contributor
3122762Claude1/**
2 * Gluecron core flow load test
3 *
4 * Tests the main public-facing pages under sustained load.
5 *
6 * Usage:
7 * k6 run scripts/load-test.js
8 *
9 * Override base URL:
10 * BASE_URL=https://gluecron.com k6 run scripts/load-test.js
11 *
12 * Staged run against staging:
13 * BASE_URL=https://staging.gluecron.com k6 run scripts/load-test.js
14 *
15 * Requirements: k6 >= 0.46 (https://k6.io/docs/get-started/installation/)
16 */
17
18import http from 'k6/http';
19import { check, sleep, group } from 'k6';
20import { Rate, Trend } from 'k6/metrics';
21
22// ---------------------------------------------------------------------------
23// Custom metrics
24// ---------------------------------------------------------------------------
25
26const errorRate = new Rate('errors');
27const landingDuration = new Trend('landing_duration');
28const exploreDuration = new Trend('explore_duration');
29const blogDuration = new Trend('blog_duration');
30const pricingDuration = new Trend('pricing_duration');
31
32// ---------------------------------------------------------------------------
33// Test options
34// ---------------------------------------------------------------------------
35
36export const options = {
37 stages: [
38 { duration: '30s', target: 100 }, // ramp up to 100 VUs
39 { duration: '1m', target: 100 }, // steady state
40 { duration: '30s', target: 0 }, // ramp down
41 ],
42 thresholds: {
43 // 95th-percentile response time under 500ms across all requests
44 http_req_duration: ['p(95)<500'],
45 // Less than 1% of requests may fail
46 http_req_failed: ['rate<0.01'],
47 // Custom error rate mirrors http_req_failed but allows per-check tracking
48 errors: ['rate<0.01'],
49 },
50};
51
52// ---------------------------------------------------------------------------
53// Helpers
54// ---------------------------------------------------------------------------
55
56const BASE_URL = __ENV.BASE_URL || 'http://localhost:3000';
57
58function get(path, params) {
59 return http.get(`${BASE_URL}${path}`, params || {});
60}
61
62// ---------------------------------------------------------------------------
63// Default function — executed once per VU per iteration
64// ---------------------------------------------------------------------------
65
66export default function () {
67 group('landing page', function () {
68 const r = get('/');
69 const ok = check(r, {
70 'landing 200': (res) => res.status === 200,
71 'landing has content': (res) => res.body && res.body.length > 500,
72 });
73 errorRate.add(!ok);
74 landingDuration.add(r.timings.duration);
75 });
76
77 sleep(0.5);
78
79 group('explore page', function () {
80 const r = get('/explore');
81 const ok = check(r, {
82 'explore 200': (res) => res.status === 200,
83 });
84 errorRate.add(!ok);
85 exploreDuration.add(r.timings.duration);
86 });
87
88 sleep(0.5);
89
90 group('blog index', function () {
91 const r = get('/blog');
92 const ok = check(r, {
93 'blog 200': (res) => res.status === 200,
94 'blog has devlog header': (res) =>
95 res.body && res.body.includes('Gluecron Devlog'),
96 });
97 errorRate.add(!ok);
98 blogDuration.add(r.timings.duration);
99 });
100
101 sleep(0.3);
102
103 group('blog post', function () {
104 const r = get('/blog/spec-to-pr-in-90-seconds');
105 const ok = check(r, {
106 'blog post 200': (res) => res.status === 200,
107 'blog post has title': (res) =>
108 res.body && res.body.includes('Spec to PR'),
109 });
110 errorRate.add(!ok);
111 blogDuration.add(r.timings.duration);
112 });
113
114 sleep(0.3);
115
116 group('pricing page', function () {
117 const r = get('/pricing');
118 const ok = check(r, {
119 'pricing 200': (res) => res.status === 200,
120 });
121 errorRate.add(!ok);
122 pricingDuration.add(r.timings.duration);
123 });
124
125 sleep(1);
126}