Skip to content

Commit 1fb1066

Browse files
esumerfdesumerfieldclaude
authored
Ensure mock server conforms to the documented API (#609)
* DEV-37524: Add mock Imperva API server for acceptance testing This commit introduces a mock server that simulates the Imperva API, enabling acceptance tests to run without requiring real API credentials. Changes include: - Mock server implementation (incapsula/mock_server.go) with in-memory storage for accounts, sites, and CSP domains - Test helper functions for easy mock server setup in tests - Standalone server CLI (cmd/mock-server/main.go) on port 19443 - GNUmakefile updates: 'make server' target and mock server check before running tests - Fixed flaky BadConnection tests in client_*_test.go files - Fixed broken HashiCorp logo in README Usage: make server # Start mock server in separate terminal make test # Run tests (requires mock server) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Conforming API responses from mock server with the documentation Updates to mock server to align with official Imperva API documentation: - Add missing fields to Account responses (plan_id, user_name, logins, trial_end_date, inactivity_timeout) - Update account configure endpoint to use param/value pattern - Add support for additional account parameters (support_all_tls_versions, wildcard_san_for_new_sites, enable_http2_for_new_sites, etc.) - Add missing fields to CSP AuthorizationStatus (note, author, reviewedAt, lastNoteAt, forceChange) - Add applyToAllOnboardedPaths field to CSP PreApprovedDomain - Fix data privacy endpoint to return 'region' instead of 'region_default' - Update default values to match Terraform schema defaults (inactivity_timeout=15, wildcard_san_for_new_sites="Default") - Add test helpers and parsing verification tests - Document mock server endpoints in README with links to API documentation - Add incapsula.test to gitignore 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Ed Sumerfield <esumerfield@stackct.com> Co-authored-by: Claude <noreply@anthropic.com>
1 parent a4faacb commit 1fb1066

13 files changed

+2233
-24
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@ examples/*.tfstate*
66
examples/ignore
77
incapsula/c.out
88
terraform-provider-incapsula
9+
mock-server
10+
incapsula.test
911
.idea/
10-
.tmp/
12+
.tmp/
13+
.claude/

GNUmakefile

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,37 @@ install: build
2929
mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${PKG_NAME}/${VERSION}/${OS_ARCH}
3030
mv ${BINARY} ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${PKG_NAME}/${VERSION}/${OS_ARCH}
3131

32-
test: fmtcheck
32+
MOCK_SERVER_PORT?=19443
33+
MOCK_SERVER_URL=http://localhost:$(MOCK_SERVER_PORT)
34+
35+
test: fmtcheck check-mock-server
3336
go test -i $(TEST) || exit 1
3437
echo $(TEST) | \
3538
xargs -t -n4 go test $(TESTARGS) -timeout=30s -parallel=4
3639

40+
check-mock-server:
41+
@if ! curl -s -o /dev/null -w '' $(MOCK_SERVER_URL)/account 2>/dev/null; then \
42+
echo ""; \
43+
echo "ERROR: Mock Imperva API server is not running on $(MOCK_SERVER_URL)"; \
44+
echo ""; \
45+
echo "To start the mock server, run in a separate terminal:"; \
46+
echo ""; \
47+
echo " make server"; \
48+
echo ""; \
49+
echo "Then set the following environment variables:"; \
50+
echo ""; \
51+
echo " export INCAPSULA_API_ID=mock-api-id"; \
52+
echo " export INCAPSULA_API_KEY=mock-api-key"; \
53+
echo " export INCAPSULA_BASE_URL=$(MOCK_SERVER_URL)"; \
54+
echo " export INCAPSULA_BASE_URL_REV_2=$(MOCK_SERVER_URL)"; \
55+
echo " export INCAPSULA_BASE_URL_REV_3=$(MOCK_SERVER_URL)"; \
56+
echo " export INCAPSULA_BASE_URL_API=$(MOCK_SERVER_URL)"; \
57+
echo " export INCAPSULA_CUSTOM_TEST_DOMAIN=.mock.incaptest.com"; \
58+
echo ""; \
59+
exit 1; \
60+
fi
61+
@echo "Mock server is running on $(MOCK_SERVER_URL)"
62+
3763
testacc: fmtcheck
3864
TF_ACC=1 go test $(TEST) -v $(TESTARGS) -timeout 120m
3965

@@ -58,6 +84,10 @@ errcheck:
5884
clean:
5985
go clean -cache -modcache -i -r
6086

87+
server:
88+
@echo "Starting mock Imperva API server..."
89+
go run ./cmd/mock-server
90+
6191
test-compile:
6292
@if [ "$(TEST)" = "./..." ]; then \
6393
echo "ERROR: Set TEST to a specific package. For example,"; \
@@ -80,5 +110,5 @@ ifeq (,$(wildcard $(GOPATH)/src/$(WEBSITE_REPO)))
80110
endif
81111
@$(MAKE) -C $(GOPATH)/src/$(WEBSITE_REPO) website-provider-test PROVIDER_PATH=$(shell pwd) PROVIDER_NAME=$(PKG_NAME)
82112

83-
.PHONY: build test testacc vet fmt fmtcheck errcheck test-compile website website-test
113+
.PHONY: build test testacc vet fmt fmtcheck errcheck test-compile website website-test server check-mock-server
84114

README.md

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Terraform `Incapsula` Provider
55
- [![Gitter chat](https://badges.gitter.im/hashicorp-terraform/Lobby.png)](https://gitter.im/hashicorp-terraform/Lobby)
66
- Mailing list: [Google Groups](http://groups.google.com/group/terraform-tool)
77

8-
<img src="https://cdn.rawgit.com/hashicorp/terraform-website/master/content/source/assets/images/logo-hashicorp.svg" width="600px">
8+
<img src="assets/HashiCorp_Logo.png" width="600px">
99

1010
Maintainers
1111
-----------
@@ -86,3 +86,95 @@ with installation command execution
8686
./tf-provider-incap-orch.sh -i "youApiID" "youApiKey"
8787
```
8888

89+
Mock Server for Testing
90+
-----------------------
91+
92+
A mock Imperva API server is provided for running tests without requiring real API credentials. This enables CI/CD pipelines and local development without access to a live Imperva environment.
93+
94+
### Starting the Mock Server
95+
96+
```sh
97+
make server
98+
```
99+
100+
This starts the mock server on port 19443. The server outputs the required environment variables:
101+
102+
```sh
103+
export INCAPSULA_API_ID=mock-api-id
104+
export INCAPSULA_API_KEY=mock-api-key
105+
export INCAPSULA_BASE_URL=http://localhost:19443
106+
export INCAPSULA_BASE_URL_REV_2=http://localhost:19443
107+
export INCAPSULA_BASE_URL_REV_3=http://localhost:19443
108+
export INCAPSULA_BASE_URL_API=http://localhost:19443
109+
export INCAPSULA_CUSTOM_TEST_DOMAIN=.mock.incaptest.com
110+
```
111+
112+
### Running Tests with Mock Server
113+
114+
```sh
115+
# Terminal 1: Start the mock server
116+
make server
117+
118+
# Terminal 2: Run tests (requires mock server to be running)
119+
make test
120+
```
121+
122+
### Implemented Endpoints
123+
124+
The mock server implements the following Imperva API endpoints:
125+
126+
#### Account Management ([Cloud v1 API Documentation](https://docs-cybersec-be.thalesgroup.com/api/bundle/api-docs/page/cloud-v1-api-definition.htm))
127+
128+
| Endpoint | Method | Description |
129+
|----------|--------|-------------|
130+
| `/accounts/add` | POST | Create account |
131+
| `/account` | POST | Get account status |
132+
| `/accounts/configure` | POST | Update account |
133+
| `/accounts/delete` | POST | Delete account |
134+
| `/accounts/data-privacy/show` | POST | Get data privacy settings |
135+
| `/accounts/data-privacy/set-region-default` | POST | Set default data region |
136+
137+
#### Site Management ([Cloud v1 API Documentation](https://docs-cybersec-be.thalesgroup.com/api/bundle/api-docs/page/cloud-v1-api-definition.htm))
138+
139+
| Endpoint | Method | Description |
140+
|----------|--------|-------------|
141+
| `/sites/add` | POST | Create site |
142+
| `/sites/status` | POST | Get site status |
143+
| `/sites/configure` | POST | Update site |
144+
| `/sites/delete` | POST | Delete site |
145+
146+
#### CSP Pre-Approved Domains ([CSP API Documentation](https://docs-cybersec-be.thalesgroup.com/api/bundle/api-docs/page/csp-api-definition.htm))
147+
148+
| Endpoint | Method | Description |
149+
|----------|--------|-------------|
150+
| `/csp-api/v1/sites/{siteId}/preapprovedlist` | GET | List pre-approved domains |
151+
| `/csp-api/v1/sites/{siteId}/preapprovedlist` | POST | Add pre-approved domain |
152+
| `/csp-api/v1/sites/{siteId}/preapprovedlist/{domainRef}` | GET | Get specific domain |
153+
| `/csp-api/v1/sites/{siteId}/preapprovedlist/{domainRef}` | DELETE | Remove domain |
154+
| `/csp-api/v1/sites/{siteId}/domains/{domainRef}/status` | GET/PUT | Domain status |
155+
| `/csp-api/v1/sites/{siteId}/domains/{domainRef}/notes` | GET/POST/DELETE | Domain notes |
156+
157+
### Response Format
158+
159+
All API responses follow the standard Imperva format:
160+
161+
```json
162+
{
163+
"res": 0,
164+
"res_message": "OK",
165+
"debug_info": {...},
166+
"account|site|data": {...}
167+
}
168+
```
169+
170+
Error responses use non-zero `res` codes as documented in the [API documentation](https://docs-cybersec-be.thalesgroup.com/api/bundle/api-docs/page/cloud-v1-api-definition.htm).
171+
172+
### Adding New Endpoints
173+
174+
To add new endpoints to the mock server:
175+
176+
1. Add the route in `mock_server.go` in the `router()` function
177+
2. Implement the handler function following existing patterns
178+
3. Add tests in `mock_server_test.go`
179+
4. Update this documentation
180+

assets/HashiCorp_Logo.png

53.8 KB
Loading

cmd/mock-server/main.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"log"
7+
"net/http"
8+
"os"
9+
"os/signal"
10+
"syscall"
11+
12+
"github.com/terraform-providers/terraform-provider-incapsula/incapsula"
13+
)
14+
15+
func main() {
16+
port := flag.Int("port", 19443, "Port to listen on")
17+
flag.Parse()
18+
19+
// Create the mock server
20+
mock := incapsula.NewMockImpervaServer()
21+
22+
// Create a new server on the specified port instead of using the httptest server
23+
mock.Server.Close() // Close the auto-started httptest server
24+
25+
addr := fmt.Sprintf(":%d", *port)
26+
server := &http.Server{
27+
Addr: addr,
28+
Handler: http.HandlerFunc(mock.ServeHTTP),
29+
}
30+
31+
// Handle shutdown gracefully
32+
done := make(chan bool)
33+
quit := make(chan os.Signal, 1)
34+
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
35+
36+
go func() {
37+
<-quit
38+
log.Println("Shutting down mock server...")
39+
done <- true
40+
}()
41+
42+
log.Printf("Mock Imperva API server starting on http://localhost%s", addr)
43+
log.Println("")
44+
log.Println("Environment variables to use with Terraform:")
45+
log.Println("")
46+
fmt.Printf("export INCAPSULA_API_ID=mock-api-id\n")
47+
fmt.Printf("export INCAPSULA_API_KEY=mock-api-key\n")
48+
fmt.Printf("export INCAPSULA_BASE_URL=http://localhost%s\n", addr)
49+
fmt.Printf("export INCAPSULA_BASE_URL_REV_2=http://localhost%s\n", addr)
50+
fmt.Printf("export INCAPSULA_BASE_URL_REV_3=http://localhost%s\n", addr)
51+
fmt.Printf("export INCAPSULA_BASE_URL_API=http://localhost%s\n", addr)
52+
fmt.Printf("export INCAPSULA_CUSTOM_TEST_DOMAIN=.mock.incaptest.com\n")
53+
log.Println("")
54+
log.Println("Press Ctrl+C to stop the server")
55+
56+
go func() {
57+
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
58+
log.Fatalf("Failed to start server: %v", err)
59+
}
60+
}()
61+
62+
<-done
63+
log.Println("Server stopped")
64+
}

incapsula/client_account_ssl_settings_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,11 @@ func TestClientGetAccountSSlSettingsBadConnection(t *testing.T) {
171171
}
172172

173173
func TestClientDeleteAccountSSlSettingsBadConnection(t *testing.T) {
174-
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://badness.incapsula.com"}
174+
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://invalid.invalid"}
175175
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
176176
diag := client.DeleteAccountSSLSettings("")
177-
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Timeout exceeded while awaiting") {
178-
t.Errorf("Should have received an time out error")
177+
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "error from Imperva service when deleting Account SSL certificate") {
178+
t.Errorf("Should have received an error, got: %v", diag)
179179
}
180180
}
181181

incapsula/client_site_certificate_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -158,23 +158,23 @@ func TestClientGetSiteCertificateRequestStatusError500(t *testing.T) {
158158
}
159159

160160
func TestClientGetSiteCertificateRequestStatusBadConnection(t *testing.T) {
161-
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://badness.incapsula.com"}
161+
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://invalid.invalid"}
162162
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
163163
updateAccountSSLSettingsResponse, diag := client.GetSiteCertificateRequestStatus(123, nil)
164-
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Timeout exceeded while awaiting") {
165-
t.Errorf("Should have received an time out error")
164+
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Failed to get request site certificate") {
165+
t.Errorf("Should have received an error, got: %v", diag)
166166
}
167167
if updateAccountSSLSettingsResponse != nil {
168168
t.Errorf("Should have received a nil addAccountResponse instance")
169169
}
170170
}
171171

172172
func TestClientDeleteRequestSiteCertificateBadConnection(t *testing.T) {
173-
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://badness.incapsula.com"}
173+
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://invalid.invalid"}
174174
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
175175
_, diag := client.DeleteRequestSiteCertificate(123, nil)
176-
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Timeout exceeded while awaiting") {
177-
t.Errorf("Should have received an time out error")
176+
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Failed to delete request site certificate") {
177+
t.Errorf("Should have received an error, got: %v", diag)
178178
}
179179
}
180180

@@ -217,11 +217,11 @@ func TestClientDeleteRequestSiteCertificateWithAccountId(t *testing.T) {
217217
}
218218

219219
func TestClientValidateDomainBadConnection(t *testing.T) {
220-
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://badness.incapsula.com"}
220+
config := &Config{APIID: "foo", APIKey: "bar", BaseURLAPI: "http://invalid.invalid"}
221221
client := &Client{config: config, httpClient: &http.Client{Timeout: time.Millisecond * 1}}
222222
diag := client.ValidateDomains(123, []int{222})
223-
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Timeout exceeded while awaiting") {
224-
t.Errorf("Should have received an time out error")
223+
if diag == nil || !diag.HasError() || !strings.Contains(diag[0].Detail, "Failed to request site SSL validation") {
224+
t.Errorf("Should have received an error, got: %v", diag)
225225
}
226226
}
227227
func TestClientValidateDomainError500(t *testing.T) {

0 commit comments

Comments
 (0)