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.jsBlame140 lines · 1 contributor
3122762Claude1/**
2 * Gluecron core flow load test
3 *
4 * Tests the main public-facing pages under sustained load.
5 *
d8cf368Claude6 * THIS FILE RUNS UNDER k6 — do not run with node or bun directly.
7 * k6 provides its own built-in modules (k6/http, k6/metrics, etc.) at runtime.
8 *
3122762Claude9 * Usage:
10 * k6 run scripts/load-test.js
11 *
12 * Override base URL:
13 * BASE_URL=https://gluecron.com k6 run scripts/load-test.js
14 *
15 * Staged run against staging:
16 * BASE_URL=https://staging.gluecron.com k6 run scripts/load-test.js
17 *
18 * Requirements: k6 >= 0.46 (https://k6.io/docs/get-started/installation/)
19 */
20
d8cf368Claude21/* global __ENV */
22
23// k6 built-in modules — loaded via k6's module system at runtime
24var http = require('k6/http');
25var k6 = require('k6');
26var metrics = require('k6/metrics');
27
28var check = k6.check;
29var sleep = k6.sleep;
30var group = k6.group;
31var Rate = metrics.Rate;
32var Trend = metrics.Trend;
3122762Claude33
34// ---------------------------------------------------------------------------
35// Custom metrics
36// ---------------------------------------------------------------------------
37
d8cf368Claude38var errorRate = new Rate('errors');
39var landingDuration = new Trend('landing_duration');
40var exploreDuration = new Trend('explore_duration');
41var blogDuration = new Trend('blog_duration');
42var pricingDuration = new Trend('pricing_duration');
3122762Claude43
44// ---------------------------------------------------------------------------
d8cf368Claude45// Test options — exported so k6 reads them before starting VUs
3122762Claude46// ---------------------------------------------------------------------------
47
d8cf368Claude48module.exports.options = {
3122762Claude49 stages: [
50 { duration: '30s', target: 100 }, // ramp up to 100 VUs
51 { duration: '1m', target: 100 }, // steady state
52 { duration: '30s', target: 0 }, // ramp down
53 ],
54 thresholds: {
55 // 95th-percentile response time under 500ms across all requests
56 http_req_duration: ['p(95)<500'],
57 // Less than 1% of requests may fail
58 http_req_failed: ['rate<0.01'],
59 // Custom error rate mirrors http_req_failed but allows per-check tracking
60 errors: ['rate<0.01'],
61 },
62};
63
64// ---------------------------------------------------------------------------
65// Helpers
66// ---------------------------------------------------------------------------
67
d8cf368Claude68var BASE_URL = (typeof __ENV !== 'undefined' && __ENV.BASE_URL) || 'http://localhost:3000';
3122762Claude69
70function get(path, params) {
d8cf368Claude71 return http.get(BASE_URL + path, params || {});
3122762Claude72}
73
74// ---------------------------------------------------------------------------
d8cf368Claude75// Default export — executed once per VU per iteration
3122762Claude76// ---------------------------------------------------------------------------
77
d8cf368Claude78module.exports.default = function () {
3122762Claude79 group('landing page', function () {
d8cf368Claude80 var r = get('/');
81 var ok = check(r, {
82 'landing 200': function (res) { return res.status === 200; },
83 'landing has content': function (res) { return res.body && res.body.length > 500; },
3122762Claude84 });
85 errorRate.add(!ok);
86 landingDuration.add(r.timings.duration);
87 });
88
89 sleep(0.5);
90
91 group('explore page', function () {
d8cf368Claude92 var r = get('/explore');
93 var ok = check(r, {
94 'explore 200': function (res) { return res.status === 200; },
3122762Claude95 });
96 errorRate.add(!ok);
97 exploreDuration.add(r.timings.duration);
98 });
99
100 sleep(0.5);
101
102 group('blog index', function () {
d8cf368Claude103 var r = get('/blog');
104 var ok = check(r, {
105 'blog 200': function (res) { return res.status === 200; },
106 'blog has devlog header': function (res) {
107 return res.body && res.body.indexOf('Gluecron Devlog') !== -1;
108 },
3122762Claude109 });
110 errorRate.add(!ok);
111 blogDuration.add(r.timings.duration);
112 });
113
114 sleep(0.3);
115
116 group('blog post', function () {
d8cf368Claude117 var r = get('/blog/spec-to-pr-in-90-seconds');
118 var ok = check(r, {
119 'blog post 200': function (res) { return res.status === 200; },
120 'blog post has title': function (res) {
121 return res.body && res.body.indexOf('Spec to PR') !== -1;
122 },
3122762Claude123 });
124 errorRate.add(!ok);
125 blogDuration.add(r.timings.duration);
126 });
127
128 sleep(0.3);
129
130 group('pricing page', function () {
d8cf368Claude131 var r = get('/pricing');
132 var ok = check(r, {
133 'pricing 200': function (res) { return res.status === 200; },
3122762Claude134 });
135 errorRate.add(!ok);
136 pricingDuration.add(r.timings.duration);
137 });
138
139 sleep(1);
d8cf368Claude140};