Skip to content

Commit a6a2d17

Browse files
cxdyjoe-elliott
andauthored
feat(ci): add go tests (joe-elliott#224)
* feat(ci): add go tests * chore(ci): improve metrics.go coverage * revert: add back the bash tests in addition to go tests * fix: remove unused test kubeconfig * ref: use dummy client for AWS, remove k8s secrets test * isolated tests with per test registries Signed-off-by: Joe Elliott <number101010@gmail.com> --------- Signed-off-by: Joe Elliott <number101010@gmail.com> Co-authored-by: Joe Elliott <number101010@gmail.com>
1 parent ae4a3bf commit a6a2d17

27 files changed

+5190
-91
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ jobs:
2525
- name: Build
2626
run: make build
2727

28+
- name: Run unit tests
29+
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
30+
31+
- name: Run integration tests
32+
run: go test -v -tags=integration ./integration_test.go
33+
2834
- name: Test certificate and kubeconfig
2935
run: |
3036
cd test/files

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,10 @@ dist/
55
test/files/certs
66
test/files/certsSibling
77
test/files/kubeConfigSibling
8-
run.sh
8+
run.sh
9+
10+
# Output of the go coverage tool
11+
*.xml
12+
*.html
13+
*.txt
14+
*.out

Makefile

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ $(GORELEASER):
99
build: $(GORELEASER)
1010
$(GORELEASER) build --skip-validate --rm-dist
1111

12+
test:
13+
go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
14+
15+
test-integration:
16+
go test -v -tags=integration ./integration_test.go
17+
18+
test-all: test test-integration
19+
1220
release-snapshot: $(GORELEASER)
1321
$(GORELEASER) release --snapshot --skip-publish --rm-dist
1422

@@ -17,5 +25,6 @@ release: $(GORELEASER)
1725

1826
clean:
1927
rm -rf dist
28+
rm -f coverage.txt
2029

21-
.PHONY: all build release-snapshot release clean
30+
.PHONY: all build test test-integration test-all release-snapshot release clean

docs/testing.md

Lines changed: 155 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,174 @@
11
# Testing
22

3-
cert-exporter testing is fairly simple. The [./test.sh](../test/files/test.sh) in the test directory will generate some certs and kubeconfigs, run the application against the files and curl the prometheus metrics to confirm they are accurate. It takes one parameter which is the number of days to expire the test certs in.
3+
cert-exporter uses both Go's built-in testing framework for unit tests and bash scripts for end-to-end integration testing.
4+
5+
## Running Tests
6+
7+
### Unit Tests
8+
9+
Run all unit tests with:
10+
11+
```bash
12+
make test
13+
```
14+
15+
Or directly with Go:
16+
17+
```bash
18+
go test -v -race ./...
19+
```
20+
21+
Unit tests cover:
22+
- Certificate parsing (PEM and PKCS12 formats)
23+
- Certificate exporter functionality
24+
- Kubeconfig parsing and certificate extraction
25+
- File-based certificate checking
26+
- Metric generation and labels
27+
- Error handling
28+
29+
### Integration Tests (Go)
30+
31+
Integration tests require a Kubernetes cluster with KUBECONFIG set:
32+
33+
```bash
34+
make test-integration
35+
```
36+
37+
Or directly with Go:
38+
39+
```bash
40+
go test -v -tags=integration ./integration_test.go
41+
```
42+
43+
Integration tests cover:
44+
- End-to-end certificate monitoring
45+
- Kubernetes secret checking
46+
- ConfigMap certificate extraction
47+
- Real Prometheus metric collection
48+
49+
### Integration Tests (Bash)
50+
51+
#### File-based Certificate Testing
52+
53+
The [test/files/test.sh](../test/files/test.sh) script generates test certificates and kubeconfigs, runs the application against the files, and curls the prometheus metrics to confirm they are accurate. It takes one parameter which is the number of days to expire the test certs in.
454

555
Example:
656

57+
```bash
58+
cd test/files
59+
./test.sh 100
60+
```
61+
62+
Output:
763
```
8-
# ./test.sh
964
** Testing Certs and kubeconfig in the same dir
1065
cert_exporter_error_total 0
1166
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certs/client.crt",issuer="root",nodename="master0"}
1267
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certs/root.crt",issuer="root",nodename="master0"}
1368
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certs/server.crt",issuer="root",nodename="master0"}
1469
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="certs/kubeconfig",name="cluster1",nodename="master0",type="cluster"}
15-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="certs/kubeconfig",name="cluster2",nodename="master0",type="cluster"}
16-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="certs/kubeconfig",name="user1",nodename="master0",type="user"}
17-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="certs/kubeconfig",name="user2",nodename="master0",type="user"}
18-
** Testing Certs and kubeconfig in sibling dirs
19-
cert_exporter_error_total 0
20-
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certsSibling/client.crt",issuer="root",nodename="master0"}
21-
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certsSibling/root.crt",issuer="root",nodename="master0"}
22-
TEST SUCCESS: cert_exporter_cert_expires_in_seconds{filename="certsSibling/server.crt",issuer="root",nodename="master0"}
23-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="kubeConfigSibling/kubeconfig",name="cluster1",nodename="master0",type="cluster"}
24-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="kubeConfigSibling/kubeconfig",name="cluster2",nodename="master0",type="cluster"}
25-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="kubeConfigSibling/kubeconfig",name="user1",nodename="master0",type="user"}
26-
TEST SUCCESS: cert_exporter_kubeconfig_expires_in_seconds{filename="kubeConfigSibling/kubeconfig",name="user2",nodename="master0",type="user"}
27-
** Testing Error metric increments
28-
E0707 09:25:59.470115 56712 periodicCertChecker.go:47] Error on certs/client.crt: Failed to parse as a pem
29-
cert_exporter_error_total 1
30-
# ./testCleanup.sh
70+
...
3171
```
3272

33-
It's not great, but it gets the job done. There could definitely be some work put into this.
34-
35-
### Dependencies
36-
73+
Dependencies:
3774
- bash
3875
- openssl
3976
- curl
4077

41-
### cert-manager testing
78+
#### cert-manager Testing
79+
80+
The [test/cert-manager/test.sh](../test/cert-manager/test.sh) script does basic testing of cert-manager created certificates in a Kubernetes cluster.
81+
82+
Requirements:
83+
- kind (Kubernetes in Docker)
84+
- kubectl
85+
- A built cert-exporter binary
86+
87+
Example:
88+
89+
```bash
90+
cd test/cert-manager
91+
./test.sh
92+
```
93+
94+
This will:
95+
1. Create a kind cluster
96+
2. Install cert-manager
97+
3. Create test certificates, secrets, configmaps, and webhooks
98+
4. Run cert-exporter against these resources
99+
5. Validate the exported metrics
100+
6. Clean up the cluster
101+
102+
### All Tests
103+
104+
Run both unit and integration tests:
105+
106+
```bash
107+
make test-all
108+
```
109+
110+
## Test Coverage
111+
112+
Generate a coverage report:
113+
114+
```bash
115+
go test -coverprofile=coverage.txt -covermode=atomic ./...
116+
go tool cover -html=coverage.txt
117+
```
118+
119+
## Test Organization
120+
121+
- **Unit tests**: Located alongside source files (e.g., `certExporter_test.go`)
122+
- **Integration tests**: Located in `integration_test.go` at the root
123+
- **Test utilities**: Located in `internal/testutil/`
124+
- `certs.go` - Certificate generation helpers
125+
- `kubeconfig.go` - Kubeconfig file builders
126+
127+
## Writing Tests
128+
129+
When adding new functionality:
130+
131+
1. Add unit tests for individual components
132+
2. Add integration tests for end-to-end flows
133+
3. Ensure tests use the test utilities in `internal/testutil/`
134+
4. Use `t.TempDir()` for temporary files
135+
5. Initialize metrics with `metrics.Init(true)` to avoid conflicts
136+
137+
### Example Unit Test
138+
139+
```go
140+
func TestCertExporter_ExportMetrics(t *testing.T) {
141+
metrics.Init(true)
142+
143+
tmpDir := testutil.CreateTempCertDir(t)
144+
certFile := filepath.Join(tmpDir, "test.crt")
145+
146+
cert := testutil.GenerateCertificate(t, testutil.CertConfig{
147+
CommonName: "test-cert",
148+
Organization: "test-org",
149+
Country: "US",
150+
Province: "CA",
151+
Days: 30,
152+
})
153+
154+
testutil.WriteCertToFile(t, cert.CertPEM, certFile)
155+
156+
exporter := &CertExporter{}
157+
err := exporter.ExportMetrics(certFile, "test-node")
158+
159+
if err != nil {
160+
t.Fatalf("ExportMetrics() failed: %v", err)
161+
}
162+
163+
// Verify metrics...
164+
}
165+
```
166+
167+
## Continuous Integration
168+
169+
Tests run automatically in GitHub Actions on:
170+
- Pull requests
171+
- Pushes to master
172+
- Tag releases
42173

43-
`./test/cert-manager-v1/test.sh` do really basic testing of cert-manager created certs.
174+
The CI pipeline runs both unit and integration tests to ensure quality.

0 commit comments

Comments
 (0)