Skip to content

Commit 12aeb4b

Browse files
committed
Improve Kasm session tests with image downloading and browser test skipping
1 parent 3e28eca commit 12aeb4b

File tree

12 files changed

+418
-60
lines changed

12 files changed

+418
-60
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ env:
2424
ADMIN_PASSWORD: ${{ secrets.ADMIN_PASSWORD }}
2525
USER_PASSWORD: ${{ secrets.USER_PASSWORD }}
2626
GO_VERSION: '1.22.0'
27+
KASM_SKIP_BROWSER_TEST: 'true' # Skip tests that require browser interaction
2728

2829
jobs:
2930
# Stage 1: Initial checks that don't need Kasm

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,5 @@ package.json
7676
branch-diff.go
7777
docs/reference docs/
7878
.github/workflows/tests.yml
79-
79+
.codeiumignore
8080
.trunk/

docs/API_IMPLEMENTATION_STATUS.md

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ These APIs are officially documented in the Kasm API documentation.
3333
| POST /api/public/join_kasm | Implemented | kasm_join | internal/resources/join || internal/resources/kasm/session/tests/session_test.go |
3434
| POST /api/public/set_session_permissions | Implemented | kasm_session_permission | internal/resources/session_permission || internal/resources/session_permission/tests/session_permission_test.go |
3535
| POST /api/public/keepalive | Implemented | kasm_keepalive | internal/resources/keepalive || internal/resources/keepalive/tests/keepalive_test.go |
36-
| POST /api/public/frame_stats | Implemented | kasm_stats | internal/resources/stats || internal/resources/stats/tests/stats_test.go |
36+
| POST /api/public/get_kasm_frame_stats | Implemented | kasm_stats | internal/client/kasm_ops.go || internal/resources/stats/tests/stats_test.go | Requires an active browser connection to the session. **Manual Testing Instructions:** Set `KASM_SKIP_BROWSER_TEST=false` and follow the prompts to open the session URL in a browser. **CI/CD Notes:** Set `KASM_SKIP_BROWSER_TEST=true` to skip in CI environments. Future work needed to automate browser interaction for CI. |
3737
| POST /api/public/screenshot | Not Implemented (Client Implementation Exists) | - | - || - |
3838
| POST /api/public/exec_command | Not Implemented (Client Implementation Exists) | - | - || - |
3939
| POST /api/public/get_kasms | Implemented | kasm_sessions | internal/datasources/sessions || internal/resources/kasm/session/tests/session_test.go |
@@ -192,10 +192,6 @@ These APIs are not officially documented in the Kasm API documentation but are u
192192
- POST /api/public/get_system_info (for kasm_system_info data source)
193193
- POST /api/public/get_system_metrics (for kasm_system_metrics data source)
194194

195-
### Implemented Undocumented APIs
196-
197-
- POST /api/public/frame_stats (for kasm_stats) - Implemented in internal/resources/stats
198-
199195
## Missing Features
200196

201197
### Missing Data Sources (Documented APIs)

docs/resources/stats.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,7 @@ terraform import kasm_stats.example <kasm_id>
6868
- Resource planning
6969
- Usage analysis
7070
- Billing information
71+
72+
5. Requirements:
73+
- An active user must be connected to the session for frame stats to be available
74+
- If no user is connected, the resource will show a warning and return empty stats

go.mod

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
module terraform-provider-kasm
22

3-
go 1.22.0
3+
go 1.24
44

55
toolchain go1.24.0
66

77
require (
8+
github.com/google/uuid v1.6.0
89
github.com/hashicorp/terraform-plugin-framework v1.12.0
910
github.com/hashicorp/terraform-plugin-framework-validators v0.14.0
1011
github.com/hashicorp/terraform-plugin-go v0.24.0
@@ -54,6 +55,7 @@ require (
5455
github.com/oklog/run v1.0.0 // indirect
5556
github.com/pmezard/go-difflib v1.0.0 // indirect
5657
github.com/rogpeppe/go-internal v1.12.0 // indirect
58+
github.com/stretchr/objx v0.5.2 // indirect
5759
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
5860
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
5961
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
@@ -62,7 +64,7 @@ require (
6264
golang.org/x/mod v0.19.0 // indirect
6365
golang.org/x/net v0.28.0 // indirect
6466
golang.org/x/sync v0.8.0 // indirect
65-
golang.org/x/sys v0.23.0 // indirect
67+
golang.org/x/sys v0.30.0 // indirect
6668
golang.org/x/text v0.17.0 // indirect
6769
golang.org/x/tools v0.23.0 // indirect
6870
google.golang.org/appengine v1.6.8 // indirect

go.sum

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
4343
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
4444
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
4545
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
46+
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
47+
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
4648
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
4749
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
4850
github.com/hashicorp/go-checkpoint v0.5.0 h1:MFYpPZCnQqQTE18jFwSII6eUQrD/oxMFp3mlgcqk5mU=
@@ -143,6 +145,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG
143145
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
144146
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
145147
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
148+
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
149+
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
146150
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
147151
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
148152
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
@@ -190,8 +194,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
190194
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
191195
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
192196
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
193-
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
194-
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
197+
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
198+
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
195199
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
196200
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
197201
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

internal/client/stats_ops.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"encoding/json"
66
"fmt"
77
"io"
8+
"log"
89
"net/http"
910
)
1011

@@ -14,6 +15,7 @@ type FrameStatsRequest struct {
1415
APISecret string `json:"api_key_secret"`
1516
KasmID string `json:"kasm_id"`
1617
UserID string `json:"user_id,omitempty"`
18+
Client string `json:"client,omitempty"`
1719
}
1820

1921
// GetFrameStats retrieves frame statistics for a specific Kasm session
@@ -26,14 +28,17 @@ func (c *Client) GetFrameStats(kasmID string, userID string) (*FrameStatsRespons
2628
APISecret: c.APISecret,
2729
KasmID: kasmID,
2830
UserID: userID,
31+
Client: "auto", // Default to auto as per documentation
2932
}
3033

3134
body, err := json.Marshal(requestBody)
3235
if err != nil {
3336
return nil, fmt.Errorf("error marshaling request body: %v", err)
3437
}
3538

36-
resp, err := c.HTTPClient.Post(c.BaseURL+"/api/public/frame_stats", "application/json", bytes.NewBuffer(body))
39+
log.Printf("[DEBUG] Getting frame stats for kasm %s", kasmID)
40+
41+
resp, err := c.HTTPClient.Post(c.BaseURL+"/api/public/get_kasm_frame_stats", "application/json", bytes.NewBuffer(body))
3742
if err != nil {
3843
return nil, fmt.Errorf("error making request: %v", err)
3944
}
@@ -49,5 +54,15 @@ func (c *Client) GetFrameStats(kasmID string, userID string) (*FrameStatsRespons
4954
return nil, fmt.Errorf("error decoding response: %v", err)
5055
}
5156

57+
// Check for error message in the response
58+
if result.ErrorMessage != "" {
59+
log.Printf("[DEBUG] Frame stats API returned error: %s", result.ErrorMessage)
60+
if result.ErrorMessage == "Error retrieving frame stats with status code (503)" ||
61+
result.ErrorMessage == "Error retrieving frame stats with status code (502)" {
62+
return nil, fmt.Errorf("frame stats unavailable: a user must be actively connected to the session")
63+
}
64+
return nil, fmt.Errorf("frame stats API error: %s", result.ErrorMessage)
65+
}
66+
5267
return &result, nil
5368
}

internal/client/stats_ops_test.go

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ func TestClient_GetFrameStats(t *testing.T) {
2020
serverResponse interface{}
2121
statusCode int
2222
expectError bool
23+
errorContains string
2324
}{
2425
{
2526
name: "successful request",
@@ -40,7 +41,8 @@ func TestClient_GetFrameStats(t *testing.T) {
4041
},
4142
},
4243
},
43-
expectError: false,
44+
expectError: false,
45+
errorContains: "",
4446
},
4547
{
4648
name: "server error",
@@ -49,6 +51,7 @@ func TestClient_GetFrameStats(t *testing.T) {
4951
statusCode: http.StatusInternalServerError,
5052
serverResponse: map[string]interface{}{"error": "internal server error"},
5153
expectError: true,
54+
errorContains: "API request failed",
5255
},
5356
{
5457
name: "invalid response",
@@ -57,6 +60,29 @@ func TestClient_GetFrameStats(t *testing.T) {
5760
statusCode: http.StatusOK,
5861
serverResponse: "invalid json",
5962
expectError: true,
63+
errorContains: "error decoding response",
64+
},
65+
{
66+
name: "503 service unavailable error",
67+
kasmID: "test-kasm-id",
68+
userID: "test-user-id",
69+
statusCode: http.StatusOK, // The API returns 200 OK with an error message
70+
serverResponse: FrameStatsResponse{
71+
ErrorMessage: "Error retrieving frame stats with status code (503)",
72+
},
73+
expectError: true,
74+
errorContains: "a user must be actively connected to the session",
75+
},
76+
{
77+
name: "502 service unavailable error",
78+
kasmID: "test-kasm-id",
79+
userID: "test-user-id",
80+
statusCode: http.StatusOK, // The API returns 200 OK with an error message
81+
serverResponse: FrameStatsResponse{
82+
ErrorMessage: "Error retrieving frame stats with status code (502)",
83+
},
84+
expectError: true,
85+
errorContains: "a user must be actively connected to the session",
6086
},
6187
}
6288

@@ -65,7 +91,7 @@ func TestClient_GetFrameStats(t *testing.T) {
6591
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
6692
// Check request method and path
6793
assert.Equal(t, http.MethodPost, r.Method)
68-
assert.Equal(t, "/api/public/frame_stats", r.URL.Path)
94+
assert.Equal(t, "/api/public/get_kasm_frame_stats", r.URL.Path)
6995

7096
// Decode request body
7197
var reqBody FrameStatsRequest
@@ -77,6 +103,7 @@ func TestClient_GetFrameStats(t *testing.T) {
77103
assert.Equal(t, "test-api-secret", reqBody.APISecret)
78104
assert.Equal(t, tc.kasmID, reqBody.KasmID)
79105
assert.Equal(t, tc.userID, reqBody.UserID)
106+
assert.Equal(t, "auto", reqBody.Client)
80107

81108
// Set response status code
82109
w.WriteHeader(tc.statusCode)
@@ -105,6 +132,7 @@ func TestClient_GetFrameStats(t *testing.T) {
105132
if tc.expectError {
106133
assert.Error(t, err)
107134
assert.Nil(t, response)
135+
assert.Contains(t, err.Error(), tc.errorContains)
108136
} else {
109137
assert.NoError(t, err)
110138
assert.NotNil(t, response)

internal/client/stats_types.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,15 @@ type FrameStats struct {
1515
}
1616

1717
type FrameStatsResponse struct {
18-
Frame FrameStats `json:"frame"`
18+
Frame FrameStats `json:"frame,omitempty"`
19+
Clients []FrameStatsClient `json:"clients,omitempty"`
20+
Analysis int `json:"analysis,omitempty"`
21+
Screenshot int `json:"screenshot,omitempty"`
22+
EncodingTotal int `json:"encoding_total,omitempty"`
23+
VideoScaling int `json:"videoscaling,omitempty"`
24+
TightJpegEncoder EncoderStats `json:"tightjpegencoder,omitempty"`
25+
TightWebpEncoder EncoderStats `json:"tightwebpencoder,omitempty"`
26+
ErrorMessage string `json:"error_message,omitempty"`
1927
}
2028

2129
type FrameStatsClient struct {

internal/resources/group_image/tests/group_image_error_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package tests
66
import (
77
"fmt"
88
"math/rand"
9+
"os"
910
"regexp"
1011
"testing"
1112
"time"

0 commit comments

Comments
 (0)