k-test-n-stress is a simple tool written with GO to facilate:
[mock]Generate fake data, with specific outputs.[request]Generate http requests to specified URLs, with a variety of options.[stress]Stress test specific URLs, with a variety of options.
ktns <command> <flags>ktns mock <flags>The mock command makes use of dynamic values which will generate mocked data depending on the desired type of data, by calling specific Mock functions.
These dynamic values must be called wrapped in (double curly braces) {{ }}, like {{ Person.name }}, which will dynamically generate a person's name.
If not wrapped, the value will be interpreted as a literal string value.
ktns mock --parse-str 'Name: {{ Person.name }}'
# Will generate
# Name: John Smithktns mock --parse-str 'Name: Person.name'
# Will generate
# Name: Person.name--list: To list all available mock functions.--parse-str: Pass a literal string to be parsed. The mock data will be generated based on the provided string.--parse-json: Pass a JSON object as a string. The mock data will be generated based on the provided object. Requires--to-stdoutor--to-json-file.--parse-json-file: Pass a path to a single.template.jsonfile (the filename must end with.template.json). The mock data will be generated based on this file. Requires--to-stdoutor--to-json-file.--generate: Pass the desired amount of root objects that will be generated (available for--parse-jsonand--parse-json-file). (More info here)--to-stdout <as-json|as-csv>: Output the result to stdout as a JSON object/array or as a CSV table. Must be used with--parse-jsonor--parse-json-file. Use--to-stdout-prettifyto format for readability. Note: CSV output works best with flat (one-level-deep) JSON objects; nested objects and arrays are serialised using their Go string representation.--to-stdout-prettify: Prettify the stdout output (indented JSON or padded-column CSV). Only valid with--to-stdout.--to-json-file [filename]: Write the result as JSON to a file. If no filename is given, defaults tooutput.jsonin the current working directory (for--parse-json) or to the template name without.templatein the same directory as the template (for--parse-json-file). When specifying an explicit filename, the=syntax is required:--to-json-file=myfile.json. Can be combined with--to-stdout.--to-csv-file [filename]: Write the result as CSV to a file. Same filename-resolution rules as--to-json-file(defaultoutput.csvin the current working directory for--parse-json, template name for--parse-json-file). When specifying an explicit filename, the=syntax is required:--to-csv-file=myfile.csv. Can be combined with--to-stdoutand--to-json-file. Note: CSV output works best with flat (one-level-deep) JSON objects.--debug: Print a live generation-progress line to stderr during large runs (workers, memory estimate, throughput, elapsed time). Writes to stderr only — stdout/file output is unaffected. Opt-in; defaultfalse.
Get a list of all the available Mock functions.
ktns mock --listktns mock --parse-str 'Hello my name is {{ Person.name }}, I am {{ Number.number::{1}:{100} }} years old.'
# Hello my name is John Smith, I am 40 years old.Output to stdout as compact JSON:
ktns mock --parse-json '{ "company": "{{ Company.name }}", "employee": { "name": "{{ Person.name }}" }}' --to-stdout as-json
# {"company":"Delvalle","employee":{"name":"Josh Smith"}}Output to stdout as prettified JSON:
ktns mock --parse-json '{ "company": "{{ Company.name }}" }' --to-stdout as-json --to-stdout-prettify
# {
# "company": "Delvalle"
# }Output to stdout as CSV:
ktns mock --parse-json '{ "name": "{{ Person.name }}", "age": "{{ Number.number::{18}:{80} }}" }' --to-stdout as-csv
# age,name
# 34,Josh SmithWrite to a file (default name output.json in the current working directory):
ktns mock --parse-json '{ "company": "{{ Company.name }}" }' --to-json-fileWrite to a specific file:
ktns mock --parse-json '{ "company": "{{ Company.name }}" }' --to-json-file=mydata.jsonBoth stdout and file simultaneously:
ktns mock --parse-json '{ "company": "{{ Company.name }}" }' --to-stdout as-json --to-json-file=mydata.jsonThe template file must end with .template.json.
// employee.template.json
{
"company": "{{ Company.name }}",
"employee": {
"name": "{{ Person.name }}",
"age": "39"
}
}Write the default output file alongside the template (employee.json):
ktns mock --parse-json-file employee.template.json --to-json-file// employee.json (written alongside the template file)
{
"company": "Delvalle",
"employee": {
"name": "Josh Smith",
"age": "39"
}
}Write to a specific file:
ktns mock --parse-json-file employee.template.json --to-json-file=myout.jsonOutput to stdout as CSV:
ktns mock --parse-json-file employee.template.json --to-stdout as-csvWith --generate to produce an array:
ktns mock --parse-json-file employee.template.json --generate 3 --to-json-file// employee.json
[
{ "company": "Delvalle", "employee": { "name": "Josh Smith", "age": "39" } },
{ "company": "Infomatics", "employee": { "name": "Jane Doe", "age": "39" } },
{ "company": "Braindance", "employee": { "name": "Sam Lee", "age": "39" } }
]Some of the Mock functions accept additional parameters. Each value parameter must be wrapped in curly braces ({value}) and separated by a colon (:).
e.g.:
{{ functionName:{arg1}:{arg2}:... }}
{
"words": "Loreum.words:{5}"
}When working with multiple parameters, you may leave them blank if not used. (They will assume default values)
// e.g.: `Number.number` expects 3 parameters (<decimal>:<min>:<max>)
// In this case <decimal> is left blank, and will use its default value.
{
"age": "Number.number::{18}:{50}"
}Use the flag --generate <number> with --parse-json or --parse-json-file to generate multiple root objects.
ktns mock --parse-json '{ "company": "{{ Company.name }}" }' --generate 10 --to-stdout as-json
# This will generate
[
{ "company": "Delvale" },
{ "company": "Infomatics" },
{ "company": "Braindance" },
...
]ktns mock --parse-json-file employees.template.json --generate 5 --to-json-file
# Writes employees.json with an array of 5 objects in the same directory as the template file.For inner objects, also pass the desired number between brackets in the object's key.
{
"phones[3]": "{{ Person.phoneNumber }}", // Will generate an array of 3 values
"employees[2]": { // Will generate an array of employees with 2 objects
"name": "{{ Person.name }}"
}
}Will produce:
{
"phones": ["...", "...", "..."],
"employees": [
{
"name": "..."
},
{
"name": "..."
}
]
}The value of a json object key may be:
- A literal raw value.
{
"name": "Some literal value"
}- A
dynamic valuevalue with the Faker function name (between double curly braces).
{
"name": "{{ Person.name }}"
}- An
object, detailing an inner object.
{
"employee": {
...
}
}- An
arrayof eitherstrings/dynamic valuesORobjects.
{
"names": ["Some name 1", "{{ Person.name }}"]
}
// Or
{
"employees": [
{ ... },
{ ... }
]
}Also create dynamic array values as detailed here.
ktns request <flags>Make use of Mock functions inside --data, --qs and --url, to mock dynamic values for the request body, query string and url params.
--method: Define the request method (GET|POST|PUT|PATCH|DELETE).--url: The request URL with added URL params. (e.g.http://localhost:3000,localhost:8000/api/users,api.com/user/{{UUID.uuidv4}})--https: A Flag to overwrite theurlprotocol to HTTPS. (If not set, it will use whichever theurlprotocol is)--header: Astring<key>: <value>pair to be used as header in the request. (Multiple flags can be used)--data: AJson string formatdefining data to be used in the request body.--qs: Astring<key>=<value>pair to be used as query string in the request. (Multiple flags can be used)--response-accessor: Astringvalue to specify how the response should be accessed, with the idea of returning a more specific segment of the response. (If unable to access, it returns the whole response)--with-metrics: A Flag to add metrics of the request in the response.--only-response-body: A Flag to force the return to only show the response's body.
ktns request --method GET --url http://localhost:3000/endpointktns request --method GET --url https://some-api.com/endpoint# The same as the previous, but forcing HTTPS with the flag, instead of informing it in the URL.
# If the flag was not used, HTTP protocol would be used by default
ktns request --method GET --https --url some-api.com/endpointktns request --method GET --url http://localhost:3000/endpoint -qs 'q=Josh'ktns request --method POST --url https://some-api.com/endpoint --data '{ "name": "John Smith" }'ktns request --method DELETE --url https://some-api.com/endpoint/20313189-596e-4a65-a706-45a1531ea317Query Strings
ktns request
--medhod GET
--url https://some-api.com/person
--qs 'ageMin={{ Number.number:{0}:{1}:{10} }}'
--qs 'ageMax={{ Number.number:{0}:{50}:{55} }}'Request Body
ktns request
--medhod POST
--url https://some-api.com/endpoint
--data '{ "name": "{{ Person.name }}", "phones[3]": "{{ Person.phoneNumber }}" }'URL Params
ktns request --method DELETE --url https://some-api.com/endpoint/{{ UUID.uuidv4 }}ktns request
--medhod GET
--url https://some-api/person
--header "Authorization: Bearer fb80ea0..."A standard response will be like:
Status: 200 OK
URL: https://some-api/objects
Headers:
Content-Length: 2731
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Server: Some
Via: 2.0 some-router
X-Ratelimit-Remaining: 48
Date: Mon, 02 May 2024 13:17:41 GMT
...
Body:
{ ... }Would include calculated metrics right bellow Status.
Status: 200 OK
Metrics:
Duration: [968.00ms]
Size: [2.67 KB]
URL: https://some-api/objects
Headers:
...
Body:
{ ... }Would result in only the response body to be shown.
{ ... }To be done.
Cobra: A commandder for modern Go CLI interations.Faker/v2: Fake data generator for Go.Gogoren: Randexp for Go.Deepcopy: Deepcopy things.Testify: Toolkit with common assertions and mocks that plays nicely with the standard library.
Provides the fake data generation engine used by the mock and request commands. It abstracts all data generation behind a single interface, keeping CLI logic decoupled from generation logic.
func New() *MockCreates a Mock instance wrapping an initialized jaswdr/faker instance. Each instance gets its own *rand.Rand source seeded from the current time, PID, and an atomic counter — guaranteeing unique seeds even when many goroutines call New() simultaneously. Instances are not goroutine-safe; each goroutine must create its own via mocker.New().
functionParams is always []string, even for numeric parameters. Each function is responsible for parsing and applying defaults.
Blank entries ("") mean "use default", enabling positional omission (e.g. Number.number::18:50 in Number.number:<decimal>:<min>:<max> — decimals left blank).
| Dependency | Used for |
|---|---|
github.com/jaswdr/faker/v2 |
~90% of functions (addresses, persons, companies, etc.) |
github.com/zach-klippenstein/goregen |
Regex.regex and Payment.creditCardCvv (via per-instance generator) |
math/rand |
Per-instance *rand.Rand in Mock — used by Company.cnpj, Person.cpf, and as the RNG source for goregen generators |
Person.cpfandCompany.cnpj: Generate random digit sequences using the instance's own*rand.Randand compute two mathematically valid checksum digits via the modulo-11 algorithm (calculateChecksuminhelpers.go).Regex.regex: Accepts a regex pattern wrapped in/…/, strips the delimiters (and unescapes\/→/), then callsregen.NewGeneratorwith the instance's*rand.Randas the RNG source to produce a matching random string.Payment.creditCardCvv: Uses aregen.Generator(created atmocker.New()time, stored on the instance) backed by the instance's*rand.Randto produce a 3-digit string — no global random source is touched.
- Add the display entry in
List()withtableLineData. - Add a
casein theGenerateswitch, parsingfunctionParamsas needed. - If it requires a utility (e.g. checksum logic), add it to
helpers.go. - Add test coverage in
helpers_test.go.
Houses all Cobra command definitions, flag parsing, input validation, and orchestration logic for every CLI subcommand. It bridges user input to the mocker package and the standard library's net/http.
// called from main.go — uses default options (os.Stdout)
func Execute()
// testable constructor
func NewRootCmd(opts *CommandOptions) *cobra.CommandCommandOptions carries a single Out io.Writer, which lets tests redirect output without touching os.Stdout. Both mock and request subcommands receive and honor this same opts.
Version in version.go is an empty var set at build time via -ldflags:
go build -ldflags "-X github.com/lfsc09/k-test-n-stress/cmd.Version=x.y.z"| File | Subcommand | Responsability |
|---|---|---|
root.go |
root | Wires subcommands, sets version, silences usage on error |
mock.go |
mock |
Flag validation, parse modes, streaming file I/O, bounded worker-pool concurrency for large generation counts |
request.go |
request |
HTTP request construction, mock injection into URL/QS/body, response formatting |
utils.go |
— | Shared CommandOptions, duration/size formatters |
version.go |
— | Build-time version variable |
| Filename Sulfix | What should test |
|---|---|
_test.go |
Unit tests for internal helpers, e.g. extractMockMethod, interpretString, processJsonMap, etc. Uses testify/suite |
_e2e_test.go |
End-to-end CLI tests via NewRootCmd with a captured bytes.Buffer as Out. Validates full flag combinations and output |
- Create
cmd/<name>.gowith aNewXxxCmd(opts *CommandOptions) *cobra.Commandconstructor. - Register it in
NewRootCmdwithrootCmd.AddCommand(NewXxxCmd(opts)). - Set
cmd.SetOut(opts.Out)inside the constructor so output is testable. - Add E2E tests in
cmd/<name>_e2e_test.gousing theexecuteCommandhelper pattern frommock_e2e_test.go.
git clone [email protected]:lfsc09/k-test-n-stress.git
cd k-test-n-stressgo mod downloadFor auto-bump version on commits.
make install-hooksgo run . <command> <flags>go test ./...Run with the race detector (required for all concurrent code):
go test -race ./...
# or via Makefile
make test-race# Download updates for all dependencies to their latest minor/patch versions
go get -u ./...
# Tidy: remove unused deps and add any missing ones
go mod tidyThis project uses a three-agent system for LLM-assisted development. Plan files live in .claude/plans/ and serve as the documentation trail for every feature, bug fix, and refactor. Audit reports live in .claude/reports/.
| Agent | File | Responsibility |
|---|---|---|
planner |
.claude/agents/planner.md |
Reads the codebase and writes a detailed step-by-step plan to .claude/plans/. Does not write code. |
developer |
.claude/agents/developer.md |
Reads a plan file, implements every step, marks each step done, and renames the file with a _[done] suffix when finished. |
auditor |
.claude/agents/auditor.md |
Independently audits the full codebase for correctness, security, architecture, performance, tests, and documentation. Writes a dated report to .claude/reports/. Does not modify any files. |
1. You describe the feature, bug, or refactor to the planner
↓
2. planner captures the current datetime, reads the codebase,
and writes .claude/plans/YYYYMMDDhhmm_<type>_<name>.md
↓
3. You review the plan (request changes if needed)
↓
4. You ask the developer to implement the plan file
↓
5. developer implements step by step, checks off each step,
renames file to YYYYMMDDhhmm_<type>_<name>_[done].md when complete
All plan files are prefixed with the datetime at creation time (YYYYMMDDhhmm):
- New features:
YYYYMMDDhhmm_feature_<short_name>.md→YYYYMMDDhhmm_feature_<short_name>_[done].md - Bug fixes:
YYYYMMDDhhmm_bug_<short_name>.md→YYYYMMDDhhmm_bug_<short_name>_[done].md - Refactors:
YYYYMMDDhhmm_refactor_<short_name>.md→YYYYMMDDhhmm_refactor_<short_name>_[done].md
The auditor runs independently of the planner/developer cycle. Invoke it at any point to get an objective snapshot of the project's health. Audit reports are written to .claude/reports/ and are intended for human review — each finding can then be handed to the planner as a specific task.
1. You ask the auditor to audit the project
↓
2. auditor reads .claude/memories.md and all source files,
then writes .claude/reports/YYYYMMDDhhmm_audit.md
↓
3. You review the report and decide which findings to act on
↓
4. You describe each chosen finding to the planner as a task
↓
5. planner → developer → done (normal pipeline)
Audit reports are prefixed with the datetime at creation time:
YYYYMMDDhhmm_audit.md
"Plan the addition of an Internet.email mock function"
→ planner writes .claude/plans/202604091530_feature_internet_email.md
"Implement .claude/plans/202604091530_feature_internet_email.md"
→ developer implements and renames to 202604091530_feature_internet_email_[done].md
"Plan a fix for the bug where --parse-files ignores --preserve-folder-structure"
→ planner writes .claude/plans/202604091530_bug_preserve_folder_structure.md
"Implement .claude/plans/202604091530_bug_preserve_folder_structure.md"
→ developer implements and renames to 202604091530_bug_preserve_folder_structure_[done].md
"Plan a refactor of mocker/helpers.go to split it into focused files"
→ planner writes .claude/plans/202604091530_refactor_mocker_helpers_split.md
"Implement .claude/plans/202604091530_refactor_mocker_helpers_split.md"
→ developer implements and renames to 202604091530_refactor_mocker_helpers_split_[done].md
"Plan the addition of an Internet.email mock function"
→ planner writes .claude/plans/202604091530_feature_internet_email.md
"Update the plan to also include Internet.url and Internet.ipv4"
→ planner updates the same plan file
"Implement .claude/plans/202604091530_feature_internet_email.md"
→ developer implements the updated plan
"Audit the project"
→ auditor reads all source files and .claude/memories.md,
writes .claude/reports/202604131200_audit.md
"Run an audit and generate a report"
→ same as above — a new dated report is always created
After reviewing the report:
// C1 finding in the report: path traversal in --to-json-file
"Plan a fix for the path traversal vulnerability in --to-json-file"
→ planner writes .claude/plans/202604131210_bug_path_traversal_json_file.md
"Implement .claude/plans/202604131210_bug_path_traversal_json_file.md"
→ developer implements and renames to …_[done].md