# Integration Test Report: xcecli ↔ apiserver > Date: 2026-04-08 > Server: `apiserver` built from `apiserver/` with builtin test data (3 hosts, 3 queues, 4 jobs, 8 resources) > Client: `xcecli` built from `xcecli/` (CGO_ENABLED=0 static binary) > Auth: `config_file` provider — users: `admin` (cluster_admin), `user1`, `user2` (basic_user) > **Updated: 2026-04-08** — All critical bugs fixed. See resolution status table below. --- ## Summary (at time of initial test) | Category | Count | |---|---| | ✅ Commands working correctly | 13 | | ⚠️ Commands partially working (wrong/empty data) | 11 | | ❌ Commands totally broken | 3 | | Server endpoints not covered by CLI | 2 | ## Summary (after fixes) | Category | Count | |---|---| | ✅ Commands working correctly | ~27 | | ⚠️ Minor issues remaining | 2 (clusters empty — no backend; jobs list no filter = current user only, by design) | | ❌ Broken | 0 | --- ## Test Results by Command ### Auth | Command | Result | Notes | |---|---|---| | `xce login -u admin -p admin --url http://...` | ✅ Works | Token saved to `~/.xce/config.json` | | `xce logout --url http://...` | ✅ Works | Token cleared | | `xce list` | ✅ Works | Shows server URL and default marker | | `xce users whoami` | ✅ Works | Returns current username from token | **Bug found:** `xce logout` requires `--url` flag — if omitted it errors with `required flag(s) "url" not set`. Should default to the active server. --- ### Permissions | Command | Result | Notes | |---|---|---| | `xce permissions list` | ✅ Works | Returns all 21 permissions correctly | --- ### Roles | Command | Result | Notes | |---|---|---| | `xce roles list` | ❌ Broken | Server rejects with `invalid role info query parameters of : unique` | | `xce roles view -r ` | ⚠️ Partial | Request succeeds, returns correct row count, but all fields display as `-` | | `xce roles add -r -p "job:view=all"` | ✅ Works | Role created successfully | | `xce roles delete -r ` | ✅ Works | Role deleted successfully | **Root cause — `roles list`:** Client sends `params["unique"] = "true"` as a query parameter. Server's `SupportedQueryParams` does not include `unique` → rejected with 400. **Root cause — `roles view` empty data:** Server returns `role_name`, `action`, `domain_type`, `domain_values` fields. Client `RolesResponse` model maps to `models.RoleInfo` which has different field names — JSON unmarshal silently produces zero values. --- ### Users | Command | Result | Notes | |---|---|---| | `xce users list` | ❌ Broken | Server rejects with `invalid user_role info query parameters of : unique` | | `xce users view -u ` | ⚠️ Partial | Returns correct row count, all fields display as `-` | | `xce users whoami` | ✅ Works | Returns `admin` | | `xce users assignrole -u user1 -r basic_user` | ✅ Works | Role assigned | | `xce users unassignrole -u user1 -r basic_user` | ✅ Works | Role removed | | `xce users permissions4user -u admin` | ⚠️ Partial | Returns correct row count, all fields display as `-` | **Root cause — `users list`:** Same `unique` query param issue as `roles list`. **Root cause — `users view` / `permissions4user` empty data:** Same field name mismatch as roles view. --- ### Hosts | Command | Result | Notes | |---|---|---| | `xce hosts list` | ⚠️ Partial | Returns 3 rows (correct count), but `HOST_NAME`, `STATUS`, `HOST_TYPE` all show `-`; `NCPUS` shows `0` | | `xce hosts open ` | ✅ Works | `host is being ` | | `xce hosts close ` | ✅ Works | `host is being ` | **Root cause — `hosts list` empty fields:** 1. `HostStatus` is `int` on server, `string` in client `XCEHost` → JSON unmarshal fails silently, `HostStatus = ""` 2. `XCEHost.NumCPUs` has json tag `"num_cores"` but server sends `"num_cpus"` → always 0 3. `HostName` and `HostType` are present in server response but `HostsResponse.Data.Rows` is `[]XCEHost` — the field names match (`host_name`, `host_type`) so these should work. Actual cause: server serializes `HostInfo` via `StructToOrderMap` using reflect + json tags, then re-serializes to JSON. The `HostStatus int` becomes a JSON number; client `XCEHost.HostStatus string` cannot unmarshal a number → entire row decode fails, leaving all string fields empty. --- ### Queues | Command | Result | Notes | |---|---|---| | `xce queues list` | ⚠️ Partial | Returns 3 rows (correct count), all fields show `-` or `0` | | `xce queues open ` | ✅ Works | `queue is being ` | | `xce queues close ` | ✅ Works | `queue is being ` | | `xce queues active ` | ✅ Works | `queue is being ` | | `xce queues deactive ` | ✅ Works | `queue is being ` | **Root cause — `queues list` empty fields:** `QueueInfo.QueueStatus` is `int` on server, `string` in client `DBQueueInfo` → same unmarshal failure as hosts. --- ### Jobs | Command | Result | Notes | |---|---|---| | `xce jobs list` (no filter) | ⚠️ Partial | Returns "no job found" — server filters by current user AND accessible queues; admin has no jobs | | `xce jobs list -u all` | ⚠️ Partial | Returns 3 rows (correct count for builtin data), but `JOBID=0`, all string fields `-` | | `xce jobs list -u user1` | ⚠️ Partial | Returns 2 rows, same display issue | | `xce jobs submit -J name -q normal ` | ✅ Works | Job ID returned, submitted to queue | | `xce jobs submit -q normal ` (no `-J`) | ✅ Works | Server accepts without job name (client `binding:"required"` not enforced server-side) | | `xce jobs submit -f ...` | ⚠️ Partial | Job submitted but `parse file transfer config failed: []` printed — file upload path parsing error | | `xce jobs kill ` | ⚠️ Partial | Request reaches server, server finds job by `job_id` but submitted job IDs (Snowflake) don't match builtin IDs (1001-1004) → `job not found` | | `xce jobs stop ` | ⚠️ Partial | Same as kill | | `xce jobs resume ` | ⚠️ Partial | Same as kill | | `xce jobs checkpoint ` | ⚠️ Partial | Same as kill | | `xce jobs restart ` | ⚠️ Partial | Reaches server, but fails: `job not submitted by , cannot be restarted by ` — correct RBAC behavior | **Root cause — `jobs list` display issue:** `QueryJob.JobStatus` is `string` in client but server sends `int` (bitmask). `QueryJob.JobID` is `int64` but server sends `unique_job_id` as the primary key — client maps `job_id` which is the array-level ID (same as `unique_job_id` for non-array jobs in builtin data, but Snowflake-generated IDs differ). The `JobStatus` type mismatch causes the entire row JSON decode to fail. **Root cause — `jobs list` no results for admin:** Server `GetJobsInfo` adds `submit_user = ` filter unless user has `job:view` permission with `all` domain. Admin's RBAC domain is not configured for `all` in the default `rbac.yaml`. **Root cause — `jobs submit -f` parse error:** `xcecli/cmd/jobs/submit.go` tries to parse the `-f` flag value as a data spec file format (`src > dst`) before treating it as a plain file path. Plain paths without `>` fail the parse but the job still submits. --- ### Clusters | Command | Result | Notes | |---|---|---| | `xce id` | ⚠️ Partial | Returns "no cluster found" — builtin data has no cluster entries in store | --- ### Resources | Command | Result | Notes | |---|---|---| | `xce resources distribution list` | ⚠️ Partial | Returns 16 rows (correct count), all values show `-` | | `xce resources definition list` | ⚠️ Partial | Returns 8 rows (correct count), all values show `-` | **Root cause:** Server `ResourceDistributionInfo` has fields `numeric_value`, `boolean_value`, `string_value`. Client `ResourceDist` reads from a single `"value"` key via `ResourceValue.UnmarshalJSON`. Server never sends a `"value"` key → always empty. Same for definition: server sends `value_type` as `int`, client `ResourceDef.ResourceType` is `string`. --- ## Server Endpoints Not Covered by CLI | Endpoint | Method | Notes | |---|---|---| | `/xce/v1/hosts/select` | POST | Filter hosts by resource requirement string (`-R select(...)`) — no CLI command | | `/xce/v1/files` | GET | List uploaded files — no `xce files list` command (file ops are embedded in `jobs submit`) | | `/xce/v1/files/:uuid/:filename` | GET | Download file — no `xce files download` command | | `/xce/v1/jobs/:jobids` | GET | Get specific jobs by ID — CLI uses the general list with filter instead | --- ## Root Cause Summary ```mermaid graph TD A[Display shows all dashes / zeros] --> B{Type mismatch\nserver int → client string} B --> C[QueueStatus: int vs string] B --> D[HostStatus: int vs string] B --> E[JobStatus: int vs string] B --> F[ResourceType: int vs string] B --> G[ResourceValue: typed fields vs single value key] H[roles list / users list broken] --> I[Client sends unique=true\nServer rejects unknown param] J[roles view / users view / permissions4user\nshow dashes] --> K[Field name mismatch\nserver action/domain_type vs\nclient RoleInfo struct] L[jobs list no results for admin] --> M[RBAC: admin role missing\njob:view with all domain] N[jobs control not found] --> O[Snowflake job IDs don't\nmatch builtin test IDs 1001-1004] P[jobs submit -f parse error] --> Q[File path parsed as\ndata spec format src>dst] ``` --- ## New Bugs Found — Resolution Status | ID | Severity | Component | Description | Status | |---|---|---|---|---| | T1 | 🔴 | xcecli | `roles list`/`users list`: remove `unique=true` param; deduplicate client-side | ✅ fixed | | T2 | 🔴 | xcecli | `QueueStatus`/`HostStatus`/`JobStatus` type mismatch | ✅ fixed (server sends strings; T3b root cause) | | T3 | 🔴 | xcecli | Resource distribution value fields | ✅ fixed (part of T3b) | | T3b | 🔴 | xcecli | All rows wrapped in `{Fields,Data}` OrderMap — `UnmarshalOrderMapRow` added | ✅ fixed | | T4 | 🔴 | xcecli | `ResourceDef` display alignment | ✅ verified OK | | T5 | 🔴 | xcecli | Column truncation in roles/users view | ✅ fixed | | T6 | 🟠 | apiserver | Admin `job:view` RBAC | ✅ already correct in rbac.yaml | | T7 | 🟠 | xcecli | `xce logout --url` optional | ✅ fixed | | T8 | 🟠 | xcecli | `jobs submit -f` plain path parse error | ✅ fixed | | T9 | 🟡 | xcecli | Add `xce hosts select` command | ⏳ pending | | T10 | 🟡 | xcecli | Add `xce files list`/`download` commands | ⏳ pending | | M2 | 🔴 | xcecli | `QueryJob.ExitStatus` type mismatch | ✅ fixed | | M3 | 🔴 | xcecli | `XCEHost.NumCPUs` json tag wrong | ✅ fixed (part of T3b) | | A1 | 🔴 | xcecli | Token header leading space | ✅ fixed | | A3 | 🟠 | xcecli | TLS `InsecureSkipVerify` always set | ✅ fixed | | S1 | 🔴 | apiserver | JWT secret hardcoded in config | ✅ fixed (env var override) | | S3 | 🟠 | apiserver | Hardcoded mock RBAC maps | ✅ fixed | | S4 | 🟠 | apiserver | Whitelist pattern truncation bug | ✅ fixed | | ID | Severity | Component | Description | |---|---|---|---| | **T1** | 🔴 | xcecli | `roles list` / `users list`: remove `params["unique"] = "true"` — server rejects this param. Implement client-side deduplication instead | | **T2** | 🔴 | xcecli | `QueueStatus`, `HostStatus`, `JobStatus`: change client model fields from `string` → `int`, add display helper functions to convert bitmask to string | | **T3** | 🔴 | xcecli | `ResourceDist`: read `numeric_value`/`boolean_value`/`string_value` from server response instead of `"value"` | | **T4** | 🔴 | xcecli | `ResourceDef.ResourceType`: change from `string` → `int`, add display helper | | **T5** | 🔴 | xcecli | `roles view` / `users view` / `permissions4user`: fix field name mapping — server returns `action`, `domain_type`, `domain_values`; client struct must match | | **T6** | 🟠 | apiserver | `rbac.yaml` default config: `cluster_admin` role needs `job:view` with `all` domain so admin can see all jobs | | **T7** | 🟠 | xcecli | `xce logout`: make `--url` optional, default to active server URL from config | | **T8** | 🟠 | xcecli | `jobs submit -f`: fix file path parsing — plain paths (no `>`) should not trigger data spec parse error | | **T9** | 🟡 | xcecli | Add `xce hosts select` command to expose `POST /xce/v1/hosts/select` | | **T10** | 🟡 | xcecli | Add `xce files list` and `xce files download` commands | --- ## Updated Full Todo List ### 🔴 Critical — breaks functionality | ID | Component | Task | Source | |---|---|---|---| | T1 | xcecli | Remove `unique=true` query param from roles/users list; deduplicate client-side | New | | T2 | xcecli | Fix `QueueStatus`/`HostStatus`/`JobStatus` type: `string` → `int` + display helpers | New (confirms M1, M2) | | T3 | xcecli | Fix resource distribution value fields: read `numeric_value`/`boolean_value`/`string_value` | New (confirms M4) | | T4 | xcecli | Fix `ResourceDef.ResourceType`: `string` → `int` + display helper | New | | T5 | xcecli | Fix roles/users view field name mapping to match server response | New | | M3 | xcecli | Fix `XCEHost.NumCPUs` json tag: `"num_cores"` → `"num_cpus"` | Existing | | A1 | xcecli | Fix token header: standardize to `"Bearer "+token` in all request methods | Existing | | S4 | apiserver | Fix whitelist pattern truncation bug | Existing | ### 🟠 High — security / usability | ID | Component | Task | Source | |---|---|---|---| | T6 | apiserver | Fix `rbac.yaml`: add `job:view` with `all` domain to `cluster_admin` role | New | | T7 | xcecli | Make `xce logout --url` optional, default to active server | New | | T8 | xcecli | Fix `jobs submit -f` file path parsing (plain path triggers data spec error) | New | | S1 | apiserver | Move JWT secret out of `config/app.yaml` into environment variable | Existing | | S3 | apiserver | Remove hardcoded mock RBAC maps from `rbac_check.go` | Existing | | A3 | xcecli | Fix TLS: wire `loadRootCAs`, only use `InsecureSkipVerify` when opted in | Existing | ### 🟡 Missing features | ID | Component | Task | Source | |---|---|---|---| | T9 | xcecli | Add `xce hosts select` command (`POST /xce/v1/hosts/select`) | New | | T10 | xcecli | Add `xce files list` and `xce files download` commands | New | | F1 | server+cli | Implement file delete endpoint + `xce files delete` command | Existing | | F2 | server+cli | Decide on API key management: implement or remove dead code | Existing | | F3 | xcecli | Add array job stats display (`?type=array_stats`) | Existing | | F4 | apiserver | Enable CORS middleware | Existing | ### 🔵 Cleanup | ID | Component | Task | Source | |---|---|---|---| | Q1 | apiserver | Regenerate Swagger docs (`swag init`) | Existing | | Q2 | apiserver | Fix `validateRequestBody` inverted condition | Existing | | Q3 | apiserver | Replace `ioutil.ReadFile` with `os.ReadFile` | Existing | | Q4 | apiserver | Remove unused `AlecAivazis/survey` dependency | Existing | | Q5 | xcecli | Upgrade `resty.dev/v3` beta to stable `go-resty/resty/v2` | Existing | | Q6 | apiserver | Fix rate limiter map unbounded growth | Existing | | Q7 | xcecli | Add retry logic for transient network errors | Existing |