A lightweight Go client utility for interacting with Codex client.
communities/codex_client.go— core HTTP client (upload/download, context-aware streaming)cmd/upload/— CLI to upload a file to Codexcmd/download/— CLI to download a file by CID.github/copilot-instructions.md— guidance for AI coding agents
We will be running codex client, and then use a small testing utility to check if the low level abstraction - CodexClient - correctly uploads and downloads the content.
I often remove some logging noise, by slightly changing the build
params in build.nims (nim-codex):
task codex, "build codex binary":
buildBinary "codex",
# params = "-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE"
params =
"-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE -d:chronicles_enabled_topics:restapi:TRACE,node:TRACE"You see a slightly more selective params in the codex task.
To run the client I use the following command:
./build/codex --data-dir=./data-1 --listen-addrs=/ip4/127.0.0.1/tcp/8081 --api-port=8001 --nat=none --disc-port=8091 --log-level=TRACEUse the following command to build the codex-upload and codex-download utilities:
go build -o bin/codex-upload ./cmd/upload
go build -o bin/codex-download ./cmd/downloadNow, using the codex-upload utility, we can upload the content to Codex as follows:
~/code/local/go-codex-client
❯ ./bin/codex-upload -file test-data.bin -host localhost -port 8001
Uploading test-data.bin (43 bytes) to Codex at localhost:8001...
✅ Upload successful!
CID: zDvZRwzm8K7bcyPeBXcZzWD7AWc4VqNuseduDr3VsuYA1yXej49VNow, having the content uploaded to Codex - let's get it back using the codex-download utility:
~/code/local/go-codex-client
❯ ./bin/codex-download -cid zDvZRwzm8K7bcyPeBXcZzWD7AWc4VqNuseduDr3VsuYA1yXej49V -file output.bin -host localhost -port 8001
Downloading CID zDvZRwzm8K7bcyPeBXcZzWD7AWc4VqNuseduDr3VsuYA1yXej49V from Codex at localhost:8001...
✅ Download successful!
Saved to: output.binYou can easily compare that the downloaded content matches the original using:
~/code/local/go-codex-client
❯ openssl sha256 test-data.bin
SHA2-256(test-data.bin)= c74ce73165c288348b168baffc477b6db38af3c629b42a7725c35d99d400d992
~/code/local/go-codex-client
❯ openssl sha256 output.bin
SHA2-256(output.bin)= c74ce73165c288348b168baffc477b6db38af3c629b42a7725c35d99d400d992We have some unit tests and a couple of integration tests.
In this section we focus on the unit tests. The integration tests are covered in the next section.
To run all unit tests:
❯ go test -v ./communities -count 1To be more selective, e.g. in order to run all the tests from
CodexArchiveDownloaderSuite, run:
go test -v ./communities -run CodexArchiveDownloader -count 1or for an individual test from that suite:
go test -v ./communities -run TestCodexArchiveDownloaderSuite/TestCancellationDuringPolling -count 1You can also use gotestsum to run the tests (you may need to install it first, e.g. go install gotest.tools/[email protected]):
gotestsum --packages="./communities" -f testname --rerun-fails -- -count 1For a more verbose output including logs use -f standard-verbose, e.g.:
gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -v -count 1To be more selective, e.g. in order to run all the tests from
CodexArchiveDownloaderSuite, run:
gotestsum --packages="./communities" -f testname --rerun-fails -- -run CodexArchiveDownloader -count 1or for an individual test from that suite:
gotestsum --packages="./communities" -f testname --rerun-fails -- -run TestCodexArchiveDownloaderSuite/TestCancellationDuringPolling -count 1Notice, that the -run flag accepts a regular expression that matches against the full test path, so you can be more concise in naming if necessary, e.g.:
gotestsum --packages="./communities" -f testname --rerun-fails -- -run CodexArchiveDownloader/Cancellation -count 1This also applies to native go test command.
When building Codex client for testing like here, I often remove some logging noise, by slightly changing the build params in build.nims:
task codex, "build codex binary":
buildBinary "codex",
# params = "-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE"
params =
"-d:chronicles_runtime_filtering -d:chronicles_log_level=TRACE -d:chronicles_enabled_topics:restapi:TRACE,node:TRACE"You see a slightly more selective params in the codex task.
To start Codex client, use e.g.:
./build/codex --data-dir=./data-1 --listen-addrs=/ip4/127.0.0.1/tcp/8081 --api-port=8001 --nat=none --disc-port=8091 --log-level=TRACETo run the integration test, use codex_integration tag and narrow the scope using -run Integration:
CODEX_API_PORT=8001 go test -v -tags=codex_integration ./communities -run Integration -timeout 15sThis will run all integration tests, including CodexClient integration tests.
To make sure that the test is actually run and not cached, use count option:
CODEX_API_PORT=8001 go test -v -tags=codex_integration ./communities -run Integration -timeout 15s -count 1To be more specific and only run the tests related to, e.g. index downloader or archive downloader you can use:
CODEX_API_PORT=8001 go test -v -tags=codex_integration ./communities -run CodexIndexDownloaderIntegration -timeout 15s -count 1
CODEX_API_PORT=8001 go test -v -tags=codex_integration ./communities -run CodexArchiveDownloaderIntegration -timeout 15s -count 1and then, if you prefer to use gotestsum:
CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -tags=codex_integration -run CodexIndexDownloaderIntegration -v -count 1
CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -tags=codex_integration -run CodexArchiveDownloaderIntegration -v -count 1or to run all integration tests (including CodexClient integration tests):
CODEX_API_PORT=8001 gotestsum --packages="./communities" -f standard-verbose --rerun-fails -- -tags=codex_integration -v -count 1 -run IntegrationI prefer to be more selective when running integration tests.
Everything you need comes included in the repo. But if you decide to change things, you will need to regenerate some artifacts. There are two:
- the protobuf
- the mocks
For the first one - protobuf - you need two components:
protoc- the Protocol Buffer compiler itselfprotoc-gen-go- the Go plugin for protoc that generates.pb.gofiles
I have followed the instructions from Protocol Buffer Compiler Installation.
The following bash script (Arch Linux) can come in handy:
#!/usr/bin/env bash
set -euo pipefail
echo "installing go..."
sudo pacman -S --noconfirm --needed go
echo "installing go protoc compiler"
PB_REL="https://github.com/protocolbuffers/protobuf/releases"
VERSION="32.1"
FILE="protoc-${VERSION}-linux-x86_64.zip"
# 1. create a temp dir
TMP_DIR="$(mktemp -d)"
# ensure cleanup on exit
trap 'rm -rf "$TMP_DIR"' EXIT
echo "Created temp dir: $TMP_DIR"
# 2. download file into temp dir
curl -L -o "$TMP_DIR/$FILE" "$PB_REL/download/v$VERSION/$FILE"
# 3. unzip into ~/.local/share/go
mkdir -p "$HOME/.local/share/go"
unzip -o "$TMP_DIR/$FILE" -d "$HOME/.local/share/go"
# 4. cleanup handled automatically by trap
echo "protoc $VERSION installed into $HOME/.local/share/go"After that make sure that $HOME/.local/share/go/bin is in your path, and you should get:
protoc --version
libprotoc 32.1The protoc-gen-go plugin is required to generate Go code from .proto files.
Install it with:
go install google.golang.org/protobuf/cmd/[email protected]Make sure $(go env GOPATH)/bin is in your $PATH so protoc can find the plugin.
Verify the installation:
which protoc-gen-go
protoc-gen-go --version
# Should output: protoc-gen-go v1.34.1In order to regenerate mocks you will need mockgen.
You can install it with:
go install go.uber.org/mock/mockgenAlso make sure you have
$(go env GOPATH)/binin your PATH. Otherwise make sure you have something likeexport PATH="$PATH:$(go env GOPATH)/bin"in your~/.bashrc(adjusted to your SHELL and OS version). This should be part of your standard GO installation.
If everything works well, you should see something like:
❯ which mockgen && mockgen -version
/home/<your-user-name>/go/bin/mockgen
v0.6.0If everything seems to be under control, we can now proceed with actual generation.
The easiest way is to regenerate all in one go:
go generate ./...If you just need to regenerate the mocks:
go generate ./communitiesIf you just need to regenerate the protobuf:
go generate ./protobuf