Skip to content

Commit e0c8e9b

Browse files
authored
feat: new session per request (#68)
1 parent 684c1dc commit e0c8e9b

10 files changed

Lines changed: 101 additions & 13 deletions

File tree

docs/docs/static/conversationsuite.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"environmentName": {
2727
"type": "string"
2828
},
29+
"newSessionPerTest": {
30+
"type": "boolean"
31+
},
2932
"tests": {
3033
"items": {
3134
"$ref": "#/$defs/TestFile"

docs/docs/static/swagger.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@
298298
"type": "string",
299299
"example": "Example Suite"
300300
},
301+
"new_session_per_test": {
302+
"type": "boolean",
303+
"example": false
304+
},
301305
"tests": {
302306
"type": "array",
303307
"items": {

docs/docs/static/swagger.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ definitions:
9898
name:
9999
example: Example Suite
100100
type: string
101+
new_session_per_test:
102+
example: false
103+
type: boolean
101104
tests:
102105
items:
103106
$ref: '#/definitions/TestRequest'

docs/docs/test-platform/test-suites.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ A Test Suite is a JSON-formatted definition that contains:
1212
- **Test Configuration**: API keys, environment settings, and Voiceflow project details
1313
- **Test Cases**: Individual test scenarios with user inputs and expected agent responses
1414
- **Validation Rules**: Criteria for determining test success or failure
15+
- **Session Management**: Control whether tests share the same user session or use isolated sessions
1516

1617
## Creating Test Suites
1718

@@ -76,6 +77,37 @@ Individual test case files referenced in the suite. You can find more details on
7677

7778
For more details on the JSON structure, refer to the [Test Suite JSON Schema](https://docs.voiceflow.com/reference/post_api-v1-tests-execute#/).
7879

80+
#### Session Management
81+
82+
Test suites support session management through the `new_session_per_test` field:
83+
84+
```json
85+
{
86+
"name": "Example Suite",
87+
"description": "Test suite with isolated sessions",
88+
"environment_name": "production",
89+
"new_session_per_test": true,
90+
"tests": [...]
91+
}
92+
```
93+
94+
**Session Behavior:**
95+
96+
- **`false` (default)**: All tests share the same user session
97+
- Variables persist across tests
98+
- Conversation context carries over between tests
99+
- Tests execute sequentially with shared state
100+
101+
- **`true`**: Each test gets a fresh user session
102+
- New user ID generated for each test
103+
- Complete isolation between tests
104+
- No shared context or variables
105+
106+
**When to use isolated sessions:**
107+
- Testing independent scenarios that shouldn't affect each other
108+
- Validating conversation flows from a fresh start
109+
- Ensuring tests don't have hidden dependencies on execution order
110+
79111
## API Integration
80112

81113
Test suites integrate with the Voiceflow API for execution:

docs/docs/tests/suites.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ name: Example Suite
1313
description: Suite used as an example
1414
# Environment name of your Voiceflow agent. It could be development, or production.
1515
environmentName: development
16+
# Optional: Create a new user session for each test (default: false)
17+
# When enabled, each test will run with a fresh user session instead of sharing one session across all tests
18+
newSessionPerTest: false
1619
# You can have multiple tests defined in separated files
1720
tests:
1821
# ID of the test.
@@ -23,6 +26,20 @@ tests:
2326
2427
It has the same structure as the NLU Profiler suite.
2528
29+
### Session Management
30+
31+
By default, all tests within a suite share the same user session (user ID). This means that:
32+
33+
- Variables set in one test persist to the next test
34+
- The conversation context carries over between tests
35+
- Tests are executed sequentially with the same user state
36+
37+
If you want each test to start with a fresh session, set `newSessionPerTest: true`. This will:
38+
39+
- Generate a new user ID for each test
40+
- Clear all conversation context between tests
41+
- Ensure tests are completely isolated from each other
42+
2643
## JSON Schema
2744

2845
`voiceflow-cli` also has a [jsonschema](http://json-schema.org/draft/2020-12/json-schema-validation.html) file, which you can use to have better

examples/suite.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
name: Example Conversation Profiler Suite
22
description: Suite used as an example
33
environmentName: production
4+
# Optional: Create a new user session for each test (default: false)
5+
# When enabled, each test will run with a fresh user session
6+
newSessionPerTest: true
47
tests:
58
- id: traditional_test
69
file: ./tests/test_1.yaml

internal/types/tests/suiteTypes.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package tests
22

33
type Suite struct {
4-
Name string `yaml:"name" json:"name"`
5-
Description string `yaml:"description" json:"description"`
6-
EnvironmentName string `yaml:"environmentName" json:"environmentName"`
7-
Tests []TestFile `yaml:"tests" json:"tests"`
8-
OpenAIConfig *OpenAIConfig `yaml:"openAIConfig,omitempty" json:"openAIConfig,omitempty"`
4+
Name string `yaml:"name" json:"name"`
5+
Description string `yaml:"description" json:"description"`
6+
EnvironmentName string `yaml:"environmentName" json:"environmentName"`
7+
NewSessionPerTest bool `yaml:"newSessionPerTest,omitempty" json:"newSessionPerTest,omitempty"`
8+
Tests []TestFile `yaml:"tests" json:"tests"`
9+
OpenAIConfig *OpenAIConfig `yaml:"openAIConfig,omitempty" json:"openAIConfig,omitempty"`
910
}
1011

1112
type TestFile struct {

pkg/test/execute.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ type HTTPSuiteRequest struct {
1414
Name string `json:"name"`
1515
Description string `json:"description"`
1616
EnvironmentName string `json:"environment_name"`
17+
NewSessionPerTest bool `json:"new_session_per_test,omitempty"` // Optional flag to create a new user session for each test (default: false)
1718
Tests []HTTPTestRequest `json:"tests"`
1819
ApiKey string `json:"api_key,omitempty"` // Optional token to override global.VoiceflowAPIKey
1920
VoiceflowSubdomain string `json:"voiceflow_subdomain,omitempty"` // Optional subdomain to override global.VoiceflowSubdomain
@@ -68,7 +69,7 @@ func ExecuteFromHTTPRequest(suiteReq HTTPSuiteRequest) *ExecuteSuiteResult {
6869
// executeHTTPSuite executes a suite from HTTP request data
6970
func executeHTTPSuite(suiteReq HTTPSuiteRequest, logCollector *LogCollector) error {
7071
// Define the user ID
71-
userID := uuid.New().String()
72+
userID := "test-" + uuid.New().String()
7273

7374
if suiteReq.VoiceflowSubdomain != "" {
7475
logCollector.AddLog("Using Voiceflow subdomain: " + suiteReq.VoiceflowSubdomain)
@@ -77,11 +78,20 @@ func executeHTTPSuite(suiteReq HTTPSuiteRequest, logCollector *LogCollector) err
7778
logCollector.AddLog("Suite: " + suiteReq.Name)
7879
logCollector.AddLog("Description: " + suiteReq.Description)
7980
logCollector.AddLog("Environment: " + suiteReq.EnvironmentName)
80-
logCollector.AddLog("User ID: " + userID)
81+
if suiteReq.NewSessionPerTest {
82+
logCollector.AddLog("New session per test: enabled")
83+
} else {
84+
logCollector.AddLog("User ID: " + userID)
85+
}
8186
logCollector.AddLog("Running Tests:")
8287

8388
// Execute each test directly from the request data
8489
for _, testReq := range suiteReq.Tests {
90+
// Create a new user ID for each test if newSessionPerTest is enabled
91+
if suiteReq.NewSessionPerTest {
92+
userID = "test-" + uuid.New().String()
93+
logCollector.AddLog("User ID for test " + testReq.ID + ": " + userID)
94+
}
8595
logCollector.AddLog("Running Test ID: " + testReq.ID)
8696
err := runTest(suiteReq.EnvironmentName, userID, testReq.Test, suiteReq.ApiKey, suiteReq.VoiceflowSubdomain, logCollector, suiteReq.OpenAIConfig)
8797
if err != nil {
@@ -97,7 +107,7 @@ func executeHTTPSuite(suiteReq HTTPSuiteRequest, logCollector *LogCollector) err
97107
func ExecuteSuite(suitesPath string) error {
98108

99109
// Define the user ID
100-
userID := uuid.New().String()
110+
userID := "test-" + uuid.New().String()
101111

102112
// Load all suites from the path
103113
suites, err := utils.LoadSuitesFromPath(suitesPath)
@@ -108,10 +118,19 @@ func ExecuteSuite(suitesPath string) error {
108118

109119
// Iterate over each suite and its tests
110120
for _, suite := range suites {
111-
global.Log.Infof("Suite: %s\nDescription: %s\nEnvironment: %s\nUser ID: %s", suite.Name, suite.Description, suite.EnvironmentName, userID)
121+
if suite.NewSessionPerTest {
122+
global.Log.Infof("Suite: %s\nDescription: %s\nEnvironment: %s\nNew session per test: enabled", suite.Name, suite.Description, suite.EnvironmentName)
123+
} else {
124+
global.Log.Infof("Suite: %s\nDescription: %s\nEnvironment: %s\nUser ID: %s", suite.Name, suite.Description, suite.EnvironmentName, userID)
125+
}
112126
global.Log.Infof("Running Tests:")
113127

114128
for _, testFile := range suite.Tests {
129+
// Create a new user ID for each test if newSessionPerTest is enabled
130+
if suite.NewSessionPerTest {
131+
userID = "test-" + uuid.New().String()
132+
global.Log.Infof("User ID for test %s: %s", testFile.ID, userID)
133+
}
115134
test, err := utils.LoadTestFromPath(testFile.File)
116135
if err != nil {
117136
global.Log.Errorf("Error loading test: %v", err)

server/docs/docs.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,10 @@ const docTemplate = `{
304304
"type": "string",
305305
"example": "Example Suite"
306306
},
307+
"new_session_per_test": {
308+
"type": "boolean",
309+
"example": false
310+
},
307311
"tests": {
308312
"type": "array",
309313
"items": {

server/handlers/handlers.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,11 @@ type TestExecutionRequest struct {
2323

2424
// TestSuiteRequest represents a test suite configuration for HTTP requests
2525
type TestSuiteRequest struct {
26-
Name string `json:"name" binding:"required" example:"Example Suite"`
27-
Description string `json:"description" example:"Suite used as an example"`
28-
EnvironmentName string `json:"environment_name" binding:"required" example:"production"`
29-
Tests []TestRequest `json:"tests" binding:"required,dive"`
26+
Name string `json:"name" binding:"required" example:"Example Suite"`
27+
Description string `json:"description" example:"Suite used as an example"`
28+
EnvironmentName string `json:"environment_name" binding:"required" example:"production"`
29+
NewSessionPerTest bool `json:"new_session_per_test" example:"false"`
30+
Tests []TestRequest `json:"tests" binding:"required,dive"`
3031
} // @name TestSuiteRequest
3132

3233
// TestRequest represents a test configuration for HTTP requests
@@ -187,6 +188,7 @@ func ExecuteTestSuite(c *gin.Context) {
187188
Name: req.Suite.Name,
188189
Description: req.Suite.Description,
189190
EnvironmentName: req.Suite.EnvironmentName,
191+
NewSessionPerTest: req.Suite.NewSessionPerTest,
190192
Tests: make([]test.HTTPTestRequest, len(req.Suite.Tests)),
191193
ApiKey: req.ApiKey, // Pass the optional ApiKey
192194
VoiceflowSubdomain: req.VoiceflowSubdomain, // Pass the optional VoiceflowSubdomain

0 commit comments

Comments
 (0)