Skip to content

Commit dcf3d9d

Browse files
author
Lucas McDonald
committed
feat(test-server): add Node.js ESDK test server
1 parent 7927d57 commit dcf3d9d

15 files changed

Lines changed: 1993 additions & 0 deletions

File tree

.github/workflows/test-server.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Separate from the library CI (ci.yml) so the test-server directory is not
2+
# swept into the coverage-gated library jobs.
3+
name: ESDK TestServer (Node.js)
4+
5+
on:
6+
pull_request:
7+
paths:
8+
- "test-server/**"
9+
- ".github/workflows/test-server.yml"
10+
push:
11+
paths:
12+
- "test-server/**"
13+
- ".github/workflows/test-server.yml"
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
javascript-language-server:
21+
name: build + test (live modules)
22+
runs-on: ubuntu-latest
23+
steps:
24+
- uses: actions/checkout@v4
25+
26+
- name: Setup Node.js
27+
uses: actions/setup-node@v4
28+
with:
29+
node-version: "22.x"
30+
cache: "npm"
31+
32+
- name: Install dependencies
33+
run: npm ci --unsafe-perm
34+
35+
- name: Build modules
36+
run: npm run build-node
37+
38+
- name: Build server
39+
working-directory: test-server
40+
run: npx tsc -p tsconfig.json
41+
42+
- name: Test server
43+
working-directory: test-server
44+
run: npm test

test-server/.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
build/
2+
.server.pid
3+
.server.log
4+
.commons-clone/

test-server/Makefile

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Target vocabulary (build-server / start-server / wait-for-server / stop-server)
2+
# matches the commons TestServer orchestration. Recipes are single shell lines
3+
# for the 3.81 Make that ships with macOS.
4+
5+
SHELL := bash
6+
7+
# Port for the server (override: make run-server PORT=9090).
8+
PORT ?= 8095
9+
10+
PID_FILE := .server.pid
11+
LOG_FILE := .server.log
12+
13+
# Bootstrap-then-delegate coordinates for `make test-server`. The commons
14+
# repository coordinates live in commons-configuration.json next to this
15+
# Makefile; COMMONS_BRANCH overrides the configured branch at invocation time.
16+
MAKEFILE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
17+
REPO_ROOT := $(abspath $(MAKEFILE_DIR)/..)
18+
COMMONS_CONFIGURATION := $(MAKEFILE_DIR)/commons-configuration.json
19+
COMMONS_BRANCH ?=
20+
CLONE_DIR ?= $(MAKEFILE_DIR)/.commons-clone
21+
CLONE_ORCH_DIR := $(CLONE_DIR)/esdk/test-server/orchestrator
22+
23+
.PHONY: help build-server run-server start-server wait-for-server stop-server \
24+
test test-server check-java clean
25+
26+
help: ## Show this help
27+
@echo "ESDK TestServer (Node.js) — make targets"
28+
@grep -E '^[a-zA-Z0-9_-]+:.*## ' "$(lastword $(MAKEFILE_LIST))" \
29+
| sort | awk 'BEGIN{FS=":.*## "}{printf " %-18s %s\n", $$1, $$2}'
30+
@echo ""
31+
@echo " PORT=$(PORT)"
32+
33+
build-server: ## Install repo deps (if needed), build the modules, compile the server
34+
@if [ ! -d "$(REPO_ROOT)/node_modules" ]; then cd "$(REPO_ROOT)" && npm ci --unsafe-perm; fi
35+
cd "$(REPO_ROOT)" && npm run build-node
36+
cd "$(MAKEFILE_DIR)" && npx tsc -p tsconfig.json
37+
38+
run-server: build-server ## Run the server in the FOREGROUND on PORT (Ctrl-C to stop)
39+
node "$(MAKEFILE_DIR)/build/src/main.js" $(PORT)
40+
41+
start-server: build-server ## Start the server in the BACKGROUND on PORT (writes .server.pid)
42+
@node "$(MAKEFILE_DIR)/build/src/main.js" $(PORT) >"$(LOG_FILE)" 2>&1 & echo $$! >"$(PID_FILE)"; \
43+
echo "started esdk-test-server (pid $$(cat $(PID_FILE))) on port $(PORT)"
44+
45+
wait-for-server: ## Block until the server accepts connections on PORT (120s timeout)
46+
@for i in $$(seq 1 120); do \
47+
if node -e 'const s=require("net").connect($(PORT),"127.0.0.1");s.on("connect",()=>{s.end();process.exit(0)});s.on("error",()=>process.exit(1))'; then \
48+
echo "server ready on $(PORT)"; exit 0; \
49+
fi; \
50+
sleep 1; \
51+
done; \
52+
echo "timed out waiting for port $(PORT)"; [ -f "$(LOG_FILE)" ] && tail -n 40 "$(LOG_FILE)"; exit 1
53+
54+
stop-server: ## Stop the background server and free PORT
55+
@if [ -f "$(PID_FILE)" ]; then kill "$$(cat $(PID_FILE))" 2>/dev/null || true; rm -f "$(PID_FILE)"; fi; \
56+
pids=$$(lsof -ti tcp:$(PORT) 2>/dev/null || true); \
57+
if [ -n "$$pids" ]; then kill $$pids 2>/dev/null || true; fi; \
58+
echo "stopped server on port $(PORT)"
59+
60+
test: build-server ## Run the server's unit/protocol tests (no AWS credentials)
61+
cd "$(MAKEFILE_DIR)" && npm test
62+
63+
check-java: ## Verify JAVA_HOME points at a JDK 21+ (the commons orchestrator needs it)
64+
@if [ -z "$$JAVA_HOME" ] || [ ! -x "$$JAVA_HOME/bin/java" ]; then \
65+
echo "ERROR: JAVA_HOME must point at a JDK 21+ for the commons orchestrator." >&2; exit 1; \
66+
fi; \
67+
v=$$("$$JAVA_HOME/bin/java" -version 2>&1 | grep -i version | head -1 | sed -E 's/.*version .?([0-9]+).*/\1/'); \
68+
if [ -z "$$v" ] || [ "$$v" -lt 21 ] 2>/dev/null; then \
69+
echo "ERROR: JDK 21+ required, JAVA_HOME has major version '$$v'." >&2; exit 1; \
70+
fi
71+
72+
# Bootstrap-then-delegate (the single orchestrated entry point): parse the
73+
# commons coordinates, clone commons at the branch head, and run the
74+
# orchestrator core in the clone with this working tree as the live JavaScript
75+
# library + server source. The core builds and launches every configured
76+
# Language_Server, runs the full Tests matrix, and tears down; its exit code
77+
# propagates. Needs AWS credentials and a JDK 21+.
78+
test-server: check-java ## Run the complete cross-language TestServer via the commons orchestrator; COMMONS_BRANCH=<b> overrides
79+
@set -eo pipefail; \
80+
if ! coords=$$(python3 -c 'import json,sys; c=json.load(open(sys.argv[1]))["commonsRepository"]; print(c["url"]); print(c["branch"])' "$(COMMONS_CONFIGURATION)" 2>/dev/null); then \
81+
echo "ERROR: missing or unparseable $(COMMONS_CONFIGURATION); halting before any clone." >&2; exit 1; \
82+
fi; \
83+
{ read -r url; read -r branch; } <<< "$$coords"; \
84+
if [ -n "$(strip $(COMMONS_BRANCH))" ]; then branch="$(strip $(COMMONS_BRANCH))"; reason="invocation-override"; else reason="configuration-entry"; fi; \
85+
echo "==> Cloning commons at '$$branch' ($$reason) into $(CLONE_DIR)"; \
86+
rm -rf "$(CLONE_DIR)"; \
87+
if ! git clone --depth 1 --single-branch --branch "$$branch" "$$url" "$(CLONE_DIR)"; then \
88+
echo "ERROR: failed to clone $$url at branch $$branch; no Tests will run." >&2; exit 1; \
89+
fi; \
90+
if [ ! -d "$(CLONE_ORCH_DIR)" ]; then \
91+
echo "ERROR: branch $$branch of $$url has no orchestrator at esdk/test-server/orchestrator." >&2; exit 1; \
92+
fi; \
93+
echo "==> Delegating: context=language:javascript languageRepoRoot=$(REPO_ROOT)"; \
94+
cd "$(CLONE_ORCH_DIR)" && ./gradlew --console=plain run \
95+
--args="context=language:javascript languageRepoRoot=$(REPO_ROOT) commonsOrigin.url=$$url commonsOrigin.branch=$$branch commonsOrigin.reason=$$reason"
96+
97+
clean: ## Remove build output, server scratch files, and the commons clone
98+
rm -rf "$(MAKEFILE_DIR)/build" "$(PID_FILE)" "$(LOG_FILE)" "$(CLONE_DIR)"

test-server/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# ESDK TestServer — Node.js Language_Server
2+
3+
A hand-implemented [rpcv2Cbor](https://smithy.io/2.0/additional-specs/protocols/smithy-rpc-v2.html)
4+
HTTP server that implements the ESDK TestServer Smithy contract and delegates
5+
each operation to the AWS Encryption SDK for JavaScript built from this
6+
repository's modules.
7+
8+
## Embedded reference to the commons TestServer
9+
10+
The single source of truth for the wire contract — the Smithy model, the one
11+
generated Test_Client, and the one Tests suite — lives in the commons repo:
12+
13+
- Repository: [`aws/aws-crypto-tools-commons`](https://github.com/aws/aws-crypto-tools-commons)
14+
- Model: `esdk/test-server/model/esdk-test-server.smithy`
15+
- Tests: `esdk/test-server/tests`
16+
17+
This repo hosts only the Node.js Language_Server; it consumes the commons
18+
contract. The commons Configuration_Set carries a `javascript` entry pointing
19+
back at this repo, closing the loop described in the TestServer factoring
20+
design.
21+
22+
## What it speaks
23+
24+
- `POST /service/ESDKTestServer/operation/{Operation}`
25+
- Header `smithy-protocol: rpc-v2-cbor`, `Content-Type: application/cbor`
26+
- CBOR map request/response bodies; errors as a CBOR map `{__type, message}`
27+
- Operations: `CreateClient`, `Encrypt`, `Decrypt`, `EncryptStream`,
28+
`DecryptStream`. The stream variants drive the library's streaming
29+
`encryptStream`/`decryptStream` APIs (this server is streaming-capable).
30+
31+
## Layout
32+
33+
- `src/cbor.ts` — self-contained CBOR codec (no new dependencies)
34+
- `src/model.ts` — wire shapes + modeled-enum ↔ library-identifier mappings
35+
- `src/bridge.ts` — modeled config → real keyrings/CMMs, operation delegation
36+
- `src/server.ts` — HTTP wire layer, routing, error mapping
37+
- `src/main.ts` — entry point (port from argv, `ESDK_TESTSERVER_PORT`, or 8095)
38+
39+
The server consumes the repo's own built modules (`@aws-crypto/client-node`)
40+
through the root workspace install; it declares no dependencies of its own and
41+
changes nothing about the published packages. Node.js >= 16 is required.
42+
43+
## Running
44+
45+
```bash
46+
make run-server PORT=8095 # foreground (builds modules first)
47+
# or, orchestrated:
48+
make start-server PORT=8095
49+
make wait-for-server PORT=8095
50+
make stop-server PORT=8095
51+
make test # unit/protocol tests, no AWS credentials
52+
```
53+
54+
## Running the full cross-language matrix
55+
56+
```bash
57+
make test-server # clones commons and delegates to its orchestrator
58+
```
59+
60+
Needs AWS credentials and a JDK 21+ (`JAVA_HOME`).
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"commonsRepository": {
3+
"name": "aws-crypto-tools-commons",
4+
"url": "git@github.com:aws/aws-crypto-tools-commons.git",
5+
"branch": "lucmcdon/esdk-test-server-all-languages"
6+
},
7+
"product": "esdk",
8+
"supportedFeatures": [
9+
"streaming",
10+
"hierarchical",
11+
"raw-aes",
12+
"raw-rsa",
13+
"multi",
14+
"aws-kms",
15+
"aws-kms-multi",
16+
"aws-kms-discovery",
17+
"aws-kms-mrk",
18+
"aws-kms-mrk-multi",
19+
"aws-kms-mrk-discovery",
20+
"caching"
21+
],
22+
"unsupportedFeatures": [
23+
"MPL",
24+
"raw-ecdh",
25+
"aws-kms-rsa",
26+
"aws-kms-ecdh",
27+
"required-encryption-context"
28+
]
29+
}

test-server/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "esdk-test-server",
3+
"private": true,
4+
"version": "0.0.1",
5+
"description": "ESDK TestServer Language_Server delegating to the AWS Encryption SDK for JavaScript (Node.js)",
6+
"scripts": {
7+
"build": "tsc -p tsconfig.json",
8+
"test": "mocha build/test/**/*.test.js"
9+
},
10+
"license": "Apache-2.0"
11+
}

0 commit comments

Comments
 (0)