Skip to content

Commit 0a4e94d

Browse files
committed
test(integrations): setup full suite of integrations tests
1 parent 6c723fd commit 0a4e94d

11 files changed

+1048
-10
lines changed

.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"-p",
2929
"8081",
3030
"--config",
31-
"tests/integrations/extended_config.integrations.yaml"
31+
"tests/integrations/webhooked_config.integrations.yaml"
3232
],
3333
"cwd": "${workspaceFolder}",
3434
"env": {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package integration_test
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/stretchr/testify/require"
9+
"github.com/stretchr/testify/suite"
10+
)
11+
12+
type ErrorScenariosIntegrationTestSuite struct {
13+
IntegrationTestSuite
14+
}
15+
16+
func (suite *ErrorScenariosIntegrationTestSuite) TestErrorScenarios() {
17+
largePayload := suite.generateLargePayload(1024 * 100) // 100KB payload
18+
largePayloadJSON, err := json.Marshal(largePayload)
19+
20+
require.NoError(suite.T(), err, "Failed to marshal large payload")
21+
22+
tests := []testInput{
23+
{
24+
name: "invalid-endpoint",
25+
endpoint: "/integration/non-existent",
26+
headers: map[string]string{
27+
"X-Token": "integration-test",
28+
},
29+
payload: map[string]any{
30+
"test": "data",
31+
},
32+
expectedResponse: expectedResponse{
33+
statusCode: 404,
34+
},
35+
},
36+
{
37+
// Must not return 400 error to don't lose data
38+
name: "invalid-json-payload",
39+
endpoint: "/integration/invalid-json-payload",
40+
headers: map[string]string{
41+
"X-Token": "integration-test",
42+
"Content-Type": "application/json",
43+
},
44+
payload: "invalid json{",
45+
expectedResponse: expectedResponse{
46+
statusCode: 204,
47+
},
48+
},
49+
{
50+
name: "missing-required-header",
51+
endpoint: "/integration/basic-usage",
52+
headers: map[string]string{}, // No X-Token header
53+
payload: map[string]any{
54+
"test": "should fail",
55+
},
56+
expectedResponse: expectedResponse{
57+
statusCode: 401,
58+
},
59+
},
60+
{
61+
name: "large-payload-handling",
62+
endpoint: "/integration/large-payload",
63+
headers: map[string]string{
64+
"X-Token": "integration-test",
65+
},
66+
payload: largePayload,
67+
expectedResponse: expectedResponse{
68+
statusCode: 204,
69+
},
70+
expectedStorage: expectedStorage{
71+
storageType: StorageTypeRedis,
72+
key: "integration:large-payload-events",
73+
data: string(largePayloadJSON),
74+
},
75+
},
76+
{
77+
name: "webhook-spec-not-found",
78+
endpoint: "/integration/missing-webhook",
79+
headers: map[string]string{
80+
"X-Token": "integration-test",
81+
},
82+
payload: map[string]any{
83+
"test": "should return 404",
84+
},
85+
expectedResponse: expectedResponse{
86+
statusCode: 404,
87+
},
88+
},
89+
}
90+
91+
for _, test := range tests {
92+
suite.Run(test.name, func() {
93+
suite.runErrorTest(test)
94+
})
95+
}
96+
}
97+
98+
func (suite *ErrorScenariosIntegrationTestSuite) runErrorTest(test testInput) {
99+
if test.name == "concurrent-requests-same-webhook" {
100+
suite.runConcurrencyTest(test)
101+
} else {
102+
suite.runTest(test)
103+
}
104+
}
105+
106+
func (suite *ErrorScenariosIntegrationTestSuite) runConcurrencyTest(test testInput) {
107+
// Send multiple concurrent requests
108+
concurrency := 10
109+
results := make(chan int, concurrency)
110+
111+
for i := 0; i < concurrency; i++ {
112+
go func(id int) {
113+
testCopy := test
114+
testCopy.headers["X-Request-ID"] = fmt.Sprintf("concurrent-%d", id)
115+
testCopy.payload = map[string]any{
116+
"request_id": id,
117+
"data": "concurrent test",
118+
}
119+
120+
suite.runTest(testCopy)
121+
results <- 1
122+
}(i)
123+
}
124+
125+
// Wait for all requests to complete
126+
for i := 0; i < concurrency; i++ {
127+
<-results
128+
}
129+
}
130+
131+
func (suite *ErrorScenariosIntegrationTestSuite) generateLargePayload(size int) map[string]any {
132+
largeString := make([]byte, size)
133+
for i := range largeString {
134+
largeString[i] = 'A' + byte(i%26)
135+
}
136+
137+
return map[string]any{
138+
"large_data": string(largeString),
139+
"metadata": map[string]any{
140+
"size": size,
141+
"timestamp": "2023-06-28T18:30:00Z",
142+
},
143+
}
144+
}
145+
146+
func TestErrorScenariosIntegrationTestSuite(t *testing.T) {
147+
suite.Run(t, new(ErrorScenariosIntegrationTestSuite))
148+
}

tests/integrations/integration_test.go

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/go-redis/redis/v8"
1414
"github.com/rs/zerolog"
1515
"github.com/rs/zerolog/log"
16+
"github.com/stretchr/testify/require"
1617
"github.com/stretchr/testify/suite"
1718
)
1819

@@ -35,13 +36,13 @@ type expectedStorage struct {
3536
storageType storageType
3637
key string
3738
data string
39+
isJson bool
3840
}
3941

4042
type storageType string
4143

4244
const (
43-
BaseURL = "http://localhost:8081/webhooks/v1alpha2"
44-
StorageTypeRedis storageType = "redis"
45+
BaseURL = "http://localhost:8081/webhooks/v1alpha2"
4546
)
4647

4748
type IntegrationTestSuite struct {
@@ -63,7 +64,6 @@ func (suite *IntegrationTestSuite) SetupSuite() {
6364
DB: 0, // use default DB
6465
Password: os.Getenv("REDIS_PASSWORD"),
6566
})
66-
6767
suite.NoError(redisclient.Ping(suite.ctx).Err(), "Failed to create Redis client")
6868
suite.NoError(redisclient.FlushDB(suite.ctx).Err(), "Failed to flush Redis database")
6969

@@ -80,6 +80,22 @@ func (suite *IntegrationTestSuite) TearDownSuite() {
8080

8181
func (suite *IntegrationTestSuite) TestIntegrationScenarios() {
8282
tests := []testInput{
83+
{
84+
name: "empty-payload",
85+
endpoint: "/integration/empty-payload",
86+
headers: map[string]string{
87+
"X-Token": "integration-test",
88+
},
89+
payload: map[string]any{},
90+
expectedResponse: expectedResponse{
91+
statusCode: 204,
92+
},
93+
expectedStorage: expectedStorage{
94+
storageType: StorageTypeRedis,
95+
key: "empty-payload:events",
96+
data: `{}`,
97+
},
98+
},
8399
{
84100
name: "basic-usage",
85101
endpoint: "/integration/basic-usage",
@@ -181,7 +197,7 @@ func (suite *IntegrationTestSuite) TestIntegrationScenarios() {
181197
}
182198
}
183199

184-
func (suite *IntegrationTestSuite) runTest(test testInput) {
200+
func (suite *IntegrationTestSuite) doRequest(test testInput) {
185201
// Prepare request
186202
jsonValue, err := json.Marshal(test.payload)
187203
suite.NoError(err, "Failed to marshal payload")
@@ -196,7 +212,7 @@ func (suite *IntegrationTestSuite) runTest(test testInput) {
196212

197213
client := &http.Client{}
198214
resp, err := client.Do(req)
199-
suite.NoError(err, "Failed to send request")
215+
require.NoError(suite.T(), err, "Failed to send request")
200216
defer resp.Body.Close()
201217

202218
// Check response status code
@@ -217,7 +233,11 @@ func (suite *IntegrationTestSuite) runTest(test testInput) {
217233
suite.Equal(test.expectedResponse.body, strings.Trim(body, "\n"), "Response body mismatch")
218234
}
219235

220-
time.Sleep(10 * time.Millisecond) // Allow some time for async processing
236+
time.Sleep(100 * time.Millisecond) // Allow some time for async processing
237+
}
238+
239+
func (suite *IntegrationTestSuite) runTest(test testInput) {
240+
suite.doRequest(test)
221241

222242
// Check storage
223243
if test.expectedStorage.storageType != "" {
@@ -231,9 +251,13 @@ func (suite *IntegrationTestSuite) runTest(test testInput) {
231251
if err != redis.Nil {
232252
suite.NoError(err, "Failed to get data from Redis")
233253
}
234-
suite.Equal(test.expectedStorage.data, data, "Data mismatch in Redis storage")
254+
255+
if test.expectedStorage.isJson {
256+
suite.JSONEq(test.expectedStorage.data, data, "Data mismatch in Redis storage")
257+
} else {
258+
suite.Equal(test.expectedStorage.data, data, "Data mismatch in Redis storage")
259+
}
235260
default:
236-
suite.Fail("Unsupported storage type: %s", test.expectedStorage.storageType)
237261
}
238262
}
239263
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package integration_test
2+
3+
func (suite *SecurityIntegrationTestSuite) TestSecurityCustomScenarios() {
4+
tests := []testInput{
5+
{
6+
name: "custom-security-valid",
7+
endpoint: "/integration/custom-security",
8+
headers: map[string]string{
9+
"Authorization": "Bearer valid-token-123",
10+
"X-API-Key": "secret-api-key",
11+
},
12+
payload: map[string]any{
13+
"data": "protected content",
14+
"type": "secure_event",
15+
},
16+
expectedResponse: expectedResponse{
17+
statusCode: 204,
18+
},
19+
expectedStorage: expectedStorage{
20+
storageType: StorageTypeRedis,
21+
key: "custom-security:events",
22+
data: `{"data":"protected content","type":"secure_event"}`,
23+
},
24+
},
25+
{
26+
name: "custom-security-invalid",
27+
endpoint: "/integration/custom-security",
28+
headers: map[string]string{
29+
"Authorization": "Bearer invalid-token",
30+
"X-API-Key": "wrong-key",
31+
},
32+
payload: map[string]any{
33+
"data": "should not be stored",
34+
},
35+
expectedResponse: expectedResponse{
36+
statusCode: 401,
37+
body: "",
38+
},
39+
expectedStorage: expectedStorage{
40+
storageType: StorageTypeRedis,
41+
key: "custom-security:events",
42+
data: "",
43+
},
44+
},
45+
}
46+
47+
for _, test := range tests {
48+
suite.Run(test.name, func() {
49+
suite.runTest(test)
50+
})
51+
}
52+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package integration_test
2+
3+
import (
4+
"crypto/hmac"
5+
"crypto/sha256"
6+
"encoding/hex"
7+
)
8+
9+
func (suite *SecurityIntegrationTestSuite) TestSecurityGithubScenarios() {
10+
tests := []testInput{
11+
{
12+
name: "github-webhook-valid-signature",
13+
endpoint: "/integration/github-webhook",
14+
headers: map[string]string{
15+
"X-Hub-Signature-256": generateGitHubSignature(`{"action":"push","ref":"refs/heads/main"}`, "github-secret"),
16+
"X-GitHub-Event": "push",
17+
},
18+
payload: map[string]any{
19+
"action": "push",
20+
"ref": "refs/heads/main",
21+
},
22+
expectedResponse: expectedResponse{
23+
statusCode: 204,
24+
},
25+
expectedStorage: expectedStorage{
26+
storageType: StorageTypeRedis,
27+
key: "github:events",
28+
data: `{"action":"push","ref":"refs/heads/main"}`,
29+
},
30+
},
31+
{
32+
name: "github-webhook-invalid-signature",
33+
endpoint: "/integration/github-webhook",
34+
headers: map[string]string{
35+
"X-Hub-Signature-256": "sha256=invalid_signature",
36+
"X-GitHub-Event": "push",
37+
},
38+
payload: map[string]any{
39+
"action": "push",
40+
"ref": "refs/heads/main",
41+
},
42+
expectedResponse: expectedResponse{
43+
statusCode: 401,
44+
},
45+
},
46+
}
47+
48+
for _, test := range tests {
49+
suite.Run(test.name, func() {
50+
suite.runTest(test)
51+
})
52+
}
53+
}
54+
55+
func generateGitHubSignature(payload, secret string) string {
56+
h := hmac.New(sha256.New, []byte(secret))
57+
h.Write([]byte(payload))
58+
return "sha256=" + hex.EncodeToString(h.Sum(nil))
59+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package integration_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/suite"
7+
)
8+
9+
type SecurityIntegrationTestSuite struct {
10+
IntegrationTestSuite
11+
}
12+
13+
func TestSecurityIntegrationTestSuite(t *testing.T) {
14+
suite.Run(t, new(SecurityIntegrationTestSuite))
15+
}

0 commit comments

Comments
 (0)