@testin-official/xagent 0.3.12 → 0.3.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +98 -2
  2. package/dist/assets/contracts/app-upload.schema.json +3 -2
  3. package/dist/assets/contracts/build-create.schema.json +17 -0
  4. package/dist/assets/contracts/build-list.schema.json +18 -0
  5. package/dist/assets/contracts/build.schema.json +76 -0
  6. package/dist/assets/contracts/execution-target.schema.json +12 -4
  7. package/dist/assets/contracts/fixtures/execution-target.concurrency-twenty-one.invalid.json +4 -0
  8. package/dist/assets/contracts/fixtures/execution-target.concurrency-two.invalid.json +1 -1
  9. package/dist/assets/contracts/fixtures/execution-target.concurrency-two.valid.json +4 -0
  10. package/dist/assets/contracts/fixtures/execution-target.count-eleven.invalid.json +6 -0
  11. package/dist/assets/contracts/fixtures/execution-target.count-two.invalid.json +1 -1
  12. package/dist/assets/contracts/fixtures/execution-target.count-two.valid.json +6 -0
  13. package/dist/assets/contracts/fixtures/execution-target.multiple-devices.invalid.json +23 -1
  14. package/dist/assets/contracts/fixtures/execution-target.multiple-devices.valid.json +4 -0
  15. package/dist/assets/contracts/fixtures/execution-target.multiple-filters.invalid.json +66 -2
  16. package/dist/assets/contracts/fixtures/execution-target.multiple-filters.valid.json +7 -0
  17. package/dist/assets/contracts/fixtures/target-hash-cases.json +52 -1
  18. package/dist/assets/contracts/fixtures/target-preview.success.json +4 -1
  19. package/dist/assets/contracts/fixtures/target-preview.unavailable.json +4 -1
  20. package/dist/assets/contracts/run-create-result.schema.json +18 -0
  21. package/dist/assets/contracts/run-create-v2.schema.json +85 -19
  22. package/dist/assets/contracts/run.schema.json +179 -34
  23. package/dist/assets/contracts/target-preview.schema.json +89 -19
  24. package/dist/cli.d.ts.map +1 -1
  25. package/dist/cli.js +324 -19
  26. package/dist/cli.js.map +1 -1
  27. package/dist/core/version.d.ts +1 -1
  28. package/dist/core/version.js +1 -1
  29. package/dist/project/target-matrix.d.ts +4 -0
  30. package/dist/project/target-matrix.d.ts.map +1 -0
  31. package/dist/project/target-matrix.js +26 -0
  32. package/dist/project/target-matrix.js.map +1 -0
  33. package/dist/run/batch.d.ts +44 -0
  34. package/dist/run/batch.d.ts.map +1 -0
  35. package/dist/run/batch.js +261 -0
  36. package/dist/run/batch.js.map +1 -0
  37. package/dist/run/builds.d.ts +20 -0
  38. package/dist/run/builds.d.ts.map +1 -0
  39. package/dist/run/builds.js +70 -0
  40. package/dist/run/builds.js.map +1 -0
  41. package/dist/run/create-result.d.ts +6 -0
  42. package/dist/run/create-result.d.ts.map +1 -0
  43. package/dist/run/create-result.js +18 -0
  44. package/dist/run/create-result.js.map +1 -0
  45. package/dist/run/exit-codes.d.ts +8 -0
  46. package/dist/run/exit-codes.d.ts.map +1 -0
  47. package/dist/run/exit-codes.js +47 -0
  48. package/dist/run/exit-codes.js.map +1 -0
  49. package/dist/run/idempotency.d.ts +2 -0
  50. package/dist/run/idempotency.d.ts.map +1 -0
  51. package/dist/run/idempotency.js +13 -0
  52. package/dist/run/idempotency.js.map +1 -0
  53. package/dist/run/junit-suite.d.ts +17 -0
  54. package/dist/run/junit-suite.d.ts.map +1 -0
  55. package/dist/run/junit-suite.js +98 -0
  56. package/dist/run/junit-suite.js.map +1 -0
  57. package/dist/run/junit.d.ts +19 -0
  58. package/dist/run/junit.d.ts.map +1 -0
  59. package/dist/run/junit.js +218 -0
  60. package/dist/run/junit.js.map +1 -0
  61. package/dist/run/scripts.d.ts +9 -0
  62. package/dist/run/scripts.d.ts.map +1 -0
  63. package/dist/run/scripts.js +156 -0
  64. package/dist/run/scripts.js.map +1 -0
  65. package/dist/sdk/client.d.ts +7 -0
  66. package/dist/sdk/client.d.ts.map +1 -1
  67. package/dist/sdk/client.js +46 -0
  68. package/dist/sdk/client.js.map +1 -1
  69. package/package.json +2 -2
package/README.md CHANGED
@@ -47,12 +47,72 @@ xagent run D:\Py\xagent-cli\examples\smoke.yaml --package-id OTHER_PACKAGE_ID --
47
47
  Project CI flow:
48
48
 
49
49
  ```powershell
50
- xagent init --upload-base-url https://files.example --token-type project_ci --name ci --token-env XAGENT_CI_TOKEN
50
+ xagent init --base-url https://api.example --upload-base-url https://files.example --token-type project_ci --name ci --token-env XAGENT_CI_TOKEN
51
+ xagent --json --profile ci --config .\xagent.yaml targets preview android-single --require-available
51
52
  xagent --json --profile ci apps upload .\app-debug.apk
52
- xagent --json --profile ci --config .\xagent.yaml run .\tests\smoke.yaml --target android-single --app-upload-id APP_UPLOAD_ID --idempotency-key PIPELINE_KEY --wait
53
+ xagent --json --profile ci --config .\xagent.yaml run .\tests\smoke.yaml --target android-single --app-upload-id APP_UPLOAD_ID --idempotency-key JOB_KEY --timeout-ms 600000 --device-wait-timeout-ms 600000 --wait --wait-timeout 1500 --run-id-file .xagent\run_id --output .xagent\run.json --junit .xagent\junit.xml
54
+ # cleanup (after_script / finally)
55
+ xagent --json --profile ci runs cancel RUN_ID --if-active
53
56
  xagent --json --profile ci apps release APP_UPLOAD_ID
54
57
  ```
55
58
 
59
+ A CI job must set both `--base-url` and `--upload-base-url` explicitly; the
60
+ defaults point to the production service.
61
+
62
+ - `targets preview --require-available` exits with `device_unavailable` (5)
63
+ when fewer devices are currently available than the Target requests, so the
64
+ job can stop before building. The preview lists the devices that would be
65
+ assigned, plus `requested_count`, `matched_count`, and `available_count`.
66
+ - Derive the idempotency key from the CI job ID (for example
67
+ `gitlab-$CI_PROJECT_ID-$CI_JOB_ID`, or
68
+ `github-$GITHUB_REPOSITORY_ID-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT-$GITHUB_JOB`).
69
+ A retried job gets a new ID and a new Run. A key reused with a new
70
+ `app_upload_id` is rejected with `idempotency_conflict` (2).
71
+ - Keep the timeouts ordered: `--device-wait-timeout-ms` (queue for a busy
72
+ device; default 10 minutes, `0` to fail immediately) plus `--timeout-ms`
73
+ (cloud execution) should be shorter than `--wait-timeout` (CLI wait), and
74
+ all of that should fit inside the CI job timeout. `run --wait` warns when
75
+ the wait is not longer than the device wait plus the Run timeout. A
76
+ device-wait timeout finishes as `infrastructure_failed` with
77
+ `reason=device_wait_timeout` and exit code 5. `targets preview
78
+ --require-available` still fails before compile when the Target cannot
79
+ start immediately, including a matrix that does not have enough free devices.
80
+ - `--run-id-file` is written right after the Run is created, so cleanup can
81
+ cancel it with `runs cancel --if-active`, which skips Runs that are already
82
+ terminal or cancelling. `--output` holds the last known Run JSON, including when
83
+ the wait times out. `--junit` writes a standard JUnit XML report for the same
84
+ Run (one script = one testcase). Failed cases include the assertion step
85
+ message. The file is written from the last known Run, including when the wait
86
+ times out.
87
+
88
+ GitLab can publish the report on the merge request test page:
89
+
90
+ ```yaml
91
+ artifacts:
92
+ when: always
93
+ paths:
94
+ - .xagent/run.json
95
+ - .xagent/junit.xml
96
+ reports:
97
+ junit: .xagent/junit.xml
98
+ ```
99
+
100
+ GitHub Actions can upload the same file and pass it to a JUnit report action:
101
+
102
+ ```yaml
103
+ - name: Upload XAgent JUnit report
104
+ if: always()
105
+ uses: actions/upload-artifact@v4
106
+ with:
107
+ name: xagent-junit
108
+ path: .xagent/junit.xml
109
+ - name: Publish XAgent JUnit report
110
+ if: always()
111
+ uses: EnricoMi/publish-unit-test-result-action@v2
112
+ with:
113
+ files: .xagent/junit.xml
114
+ ```
115
+
56
116
  File-service upload requests default to a 15-minute timeout. Override it when needed with
57
117
  `xagent apps upload .\app-debug.apk --upload-timeout 900` or
58
118
  `xagent run tests\smoke.yaml --apk .\app-debug.apk --device-id DEVICE_001 --upload-timeout 900`.
@@ -60,6 +120,42 @@ File-service upload requests default to a 15-minute timeout. Override it when ne
60
120
  `apps upload` and `apps release` require a newly issued `project_ci` Token with
61
121
  the `app:upload` capability. One upload can be bound to one Run. Release is
62
122
  idempotent and should run from CI cleanup (`after_script` / `finally`).
123
+
124
+ To run several scripts in one CI job, promote the upload once, then pass a
125
+ directory (or multiple YAML files) to `run`. The CLI starts a Build when you
126
+ pass `--build auto`, attaches each script as a Run, and reuses the same
127
+ `package_id` so the APK is not uploaded again. Derived idempotency keys are
128
+ `<job-key>:<relative-path>:<target>` (the suffix is hashed if the key would
129
+ exceed 255 characters). `--max-parallel` defaults to 1 and only limits how
130
+ many scripts the CLI submits at once. A Target may name up to 20 fixed
131
+ devices or up to 10 filters (`count` 1-10, `concurrency` 1-20). The service
132
+ expands that matrix into one single-device Run per device under the same
133
+ build; `concurrency` is how many of those Runs may occupy a device at the
134
+ same time, and the rest wait using the device-wait queue. A matrix Target requires
135
+ `--package-id` (or `--app-upload-id --promote`) and a build; `run` starts
136
+ one automatically when you omit `--build-id` and `--build auto`.
137
+
138
+ ```powershell
139
+ xagent --json --profile ci apps upload .\app-debug.apk --promote
140
+ xagent --json --profile ci --config .\xagent.yaml run .\tests --target android-single --package-id PACKAGE_ID --build auto --idempotency-key JOB_KEY --timeout-ms 600000 --device-wait-timeout-ms 600000 --wait --wait-timeout 1500 --output .xagent\run.json --junit .xagent\junit.xml
141
+ # cleanup (after_script / finally)
142
+ xagent --json --profile ci builds cancel BUILD_ID --if-active
143
+ ```
144
+
145
+ `--build auto` infers the build name, commit, and pipeline URL from GitLab CI
146
+ or GitHub Actions environment variables. You can also pass an existing
147
+ `--build-id`, or manage the same resource with `builds start`, `builds show`,
148
+ `builds wait`, and `builds cancel`. A multi-script `--junit` (or
149
+ `builds wait --junit`) writes a suite report: one `testsuites`, one
150
+ `testsuite` per target, one `testcase` per script and device. A single-script, single-device
151
+ `run --wait --junit` is unchanged. The command's exit code follows the D3
152
+ priority 3 > 2 > 6 > 1 > 7 > 5 > 4 > 0. `--output` for a batch is
153
+ `{ "build_id", "runs": [...] }`.
154
+
155
+ `apps promote APP_UPLOAD_ID` does the same for an existing upload. A promoted
156
+ package is kept in the project until it is deleted on the platform; `apps release`
157
+ on a promoted upload leaves it in place. A promoted upload can no longer be
158
+ passed as `--app-upload-id`.
63
159
  The Profile's `upload_base_url` points to the file service. The CLI sends the
64
160
  same Developer Token only to `/app/ci/init`; chunk requests use a short-lived
65
161
  upload ticket and never persist the Token in project files.
@@ -20,7 +20,7 @@
20
20
  "id": {"type": "string", "pattern": "^appup_[a-f0-9]{32}$"},
21
21
  "package_id": {"type": "integer", "minimum": 1},
22
22
  "status": {
23
- "enum": ["active", "bound", "release_requested", "released", "expired"]
23
+ "enum": ["active", "bound", "release_requested", "released", "expired", "promoted"]
24
24
  },
25
25
  "file_name": {"type": "string", "minLength": 1, "maxLength": 512},
26
26
  "sha256": {"type": "string", "pattern": "^[a-f0-9]{64}$"},
@@ -40,6 +40,7 @@
40
40
  "expires_at": {"type": "string", "format": "date-time"},
41
41
  "bound_run_id": {"type": "string", "minLength": 1},
42
42
  "release_requested_at": {"type": "string", "format": "date-time"},
43
- "released_at": {"type": "string", "format": "date-time"}
43
+ "released_at": {"type": "string", "format": "date-time"},
44
+ "promoted_at": {"type": "string", "format": "date-time"}
44
45
  }
45
46
  }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build-create.schema.json",
4
+ "title": "XAgent Build Create v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-build-create/v1"},
10
+ "name": {"type": "string", "minLength": 1, "maxLength": 255},
11
+ "external_id": {"type": "string", "minLength": 1, "maxLength": 255},
12
+ "source": {"enum": ["gitlab", "github", "other"]},
13
+ "commit_sha": {"type": "string", "minLength": 1, "maxLength": 64},
14
+ "ref": {"type": "string", "minLength": 1, "maxLength": 255},
15
+ "pipeline_url": {"type": "string", "format": "uri", "maxLength": 1024}
16
+ }
17
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build-list.schema.json",
4
+ "title": "XAgent Build List v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "builds", "page", "page_size", "total"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-build-list/v1"},
10
+ "builds": {
11
+ "type": "array",
12
+ "items": {"$ref": "build.schema.json"}
13
+ },
14
+ "page": {"type": "integer", "minimum": 1},
15
+ "page_size": {"type": "integer", "minimum": 1, "maximum": 100},
16
+ "total": {"type": "integer", "minimum": 0}
17
+ }
18
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/build.schema.json",
4
+ "title": "XAgent Build v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schema_version",
9
+ "id",
10
+ "status",
11
+ "name",
12
+ "closed",
13
+ "cancel_requested",
14
+ "created_at",
15
+ "runs"
16
+ ],
17
+ "properties": {
18
+ "schema_version": {"const": "xagent-build/v1"},
19
+ "id": {"type": "string", "pattern": "^bld_[a-f0-9]{32}$"},
20
+ "status": {
21
+ "enum": [
22
+ "running",
23
+ "passed",
24
+ "failed",
25
+ "infrastructure_failed",
26
+ "cancelled",
27
+ "timed_out"
28
+ ]
29
+ },
30
+ "name": {"type": "string", "minLength": 1, "maxLength": 255},
31
+ "external_id": {"type": "string", "minLength": 1, "maxLength": 255},
32
+ "source": {"enum": ["gitlab", "github", "other"]},
33
+ "commit_sha": {"type": "string", "minLength": 1, "maxLength": 64},
34
+ "ref": {"type": "string", "minLength": 1, "maxLength": 255},
35
+ "pipeline_url": {"type": "string", "format": "uri", "maxLength": 1024},
36
+ "closed": {"type": "boolean"},
37
+ "cancel_requested": {"type": "boolean"},
38
+ "created_at": {"type": "string", "format": "date-time"},
39
+ "closed_at": {"type": "string", "format": "date-time"},
40
+ "runs": {
41
+ "type": "array",
42
+ "items": {"$ref": "#/$defs/run_summary"}
43
+ },
44
+ "cancel_results": {
45
+ "type": "array",
46
+ "items": {"$ref": "#/$defs/cancel_result"}
47
+ }
48
+ },
49
+ "$defs": {
50
+ "run_summary": {
51
+ "type": "object",
52
+ "additionalProperties": false,
53
+ "required": ["id", "status"],
54
+ "properties": {
55
+ "id": {"type": "string", "minLength": 1},
56
+ "script_name": {"type": "string"},
57
+ "target_name": {"type": "string"},
58
+ "device_id": {"type": "string"},
59
+ "device_model": {"type": "string"},
60
+ "status": {"type": "string", "minLength": 1},
61
+ "exit_code": {"type": ["integer", "null"], "minimum": 0, "maximum": 7}
62
+ }
63
+ },
64
+ "cancel_result": {
65
+ "type": "object",
66
+ "additionalProperties": false,
67
+ "required": ["id", "ok"],
68
+ "properties": {
69
+ "id": {"type": "string", "minLength": 1},
70
+ "ok": {"type": "boolean"},
71
+ "status": {"type": "string"},
72
+ "error": {"type": "string"}
73
+ }
74
+ }
75
+ }
76
+ }
@@ -9,17 +9,21 @@
9
9
  "device_ids": {
10
10
  "type": "array",
11
11
  "minItems": 1,
12
- "maxItems": 1,
12
+ "maxItems": 20,
13
13
  "uniqueItems": true,
14
14
  "items": {"type": "string", "minLength": 1, "maxLength": 128}
15
15
  },
16
16
  "device_filters": {
17
17
  "type": "array",
18
18
  "minItems": 1,
19
- "maxItems": 1,
19
+ "maxItems": 10,
20
20
  "items": {"$ref": "#/$defs/device_filter"}
21
21
  },
22
- "concurrency": {"const": 1}
22
+ "concurrency": {
23
+ "type": "integer",
24
+ "minimum": 1,
25
+ "maximum": 20
26
+ }
23
27
  },
24
28
  "oneOf": [
25
29
  {
@@ -41,7 +45,11 @@
41
45
  "brand": {"type": "string", "minLength": 1, "maxLength": 128},
42
46
  "model": {"type": "string", "minLength": 1, "maxLength": 128},
43
47
  "resolution": {"type": "string", "minLength": 1, "maxLength": 64},
44
- "count": {"const": 1},
48
+ "count": {
49
+ "type": "integer",
50
+ "minimum": 1,
51
+ "maximum": 10
52
+ },
45
53
  "strategy": {"enum": ["random", "weight"]}
46
54
  },
47
55
  "anyOf": [
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001"],
3
+ "concurrency": 21
4
+ }
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "device_ids": ["DEVICE_001"],
3
- "concurrency": 2
3
+ "concurrency": 21
4
4
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001"],
3
+ "concurrency": 2
4
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "count": 11, "strategy": "random"}
4
+ ],
5
+ "concurrency": 1
6
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "device_filters": [
3
- {"os": "Android", "count": 2, "strategy": "random"}
3
+ {"os": "Android", "count": 11, "strategy": "random"}
4
4
  ],
5
5
  "concurrency": 1
6
6
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "count": 2, "strategy": "random"}
4
+ ],
5
+ "concurrency": 2
6
+ }
@@ -1,4 +1,26 @@
1
1
  {
2
- "device_ids": ["DEVICE_001", "DEVICE_002"],
2
+ "device_ids": [
3
+ "DEVICE_001",
4
+ "DEVICE_002",
5
+ "DEVICE_003",
6
+ "DEVICE_004",
7
+ "DEVICE_005",
8
+ "DEVICE_006",
9
+ "DEVICE_007",
10
+ "DEVICE_008",
11
+ "DEVICE_009",
12
+ "DEVICE_010",
13
+ "DEVICE_011",
14
+ "DEVICE_012",
15
+ "DEVICE_013",
16
+ "DEVICE_014",
17
+ "DEVICE_015",
18
+ "DEVICE_016",
19
+ "DEVICE_017",
20
+ "DEVICE_018",
21
+ "DEVICE_019",
22
+ "DEVICE_020",
23
+ "DEVICE_021"
24
+ ],
3
25
  "concurrency": 1
4
26
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "device_ids": ["DEVICE_001", "DEVICE_002"],
3
+ "concurrency": 1
4
+ }
@@ -1,7 +1,71 @@
1
1
  {
2
2
  "device_filters": [
3
- {"os": "Android", "brand": "Huawei", "count": 1, "strategy": "weight"},
4
- {"os": "Android", "brand": "Samsung", "count": 1, "strategy": "weight"}
3
+ {
4
+ "os": "Android",
5
+ "brand": "Brand1",
6
+ "count": 1,
7
+ "strategy": "weight"
8
+ },
9
+ {
10
+ "os": "Android",
11
+ "brand": "Brand2",
12
+ "count": 1,
13
+ "strategy": "weight"
14
+ },
15
+ {
16
+ "os": "Android",
17
+ "brand": "Brand3",
18
+ "count": 1,
19
+ "strategy": "weight"
20
+ },
21
+ {
22
+ "os": "Android",
23
+ "brand": "Brand4",
24
+ "count": 1,
25
+ "strategy": "weight"
26
+ },
27
+ {
28
+ "os": "Android",
29
+ "brand": "Brand5",
30
+ "count": 1,
31
+ "strategy": "weight"
32
+ },
33
+ {
34
+ "os": "Android",
35
+ "brand": "Brand6",
36
+ "count": 1,
37
+ "strategy": "weight"
38
+ },
39
+ {
40
+ "os": "Android",
41
+ "brand": "Brand7",
42
+ "count": 1,
43
+ "strategy": "weight"
44
+ },
45
+ {
46
+ "os": "Android",
47
+ "brand": "Brand8",
48
+ "count": 1,
49
+ "strategy": "weight"
50
+ },
51
+ {
52
+ "os": "Android",
53
+ "brand": "Brand9",
54
+ "count": 1,
55
+ "strategy": "weight"
56
+ },
57
+ {
58
+ "os": "Android",
59
+ "brand": "Brand10",
60
+ "count": 1,
61
+ "strategy": "weight"
62
+ },
63
+ {
64
+ "os": "Android",
65
+ "brand": "Brand11",
66
+ "count": 1,
67
+ "strategy": "weight"
68
+ }
5
69
  ],
6
70
  "concurrency": 1
7
71
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "device_filters": [
3
+ {"os": "Android", "brand": "Huawei", "count": 1, "strategy": "weight"},
4
+ {"os": "Android", "brand": "Samsung", "count": 1, "strategy": "weight"}
5
+ ],
6
+ "concurrency": 2
7
+ }
@@ -3,7 +3,9 @@
3
3
  {
4
4
  "name": "fixed_device",
5
5
  "target": {
6
- "device_ids": ["DEVICE_001"],
6
+ "device_ids": [
7
+ "DEVICE_001"
8
+ ],
7
9
  "concurrency": 1
8
10
  },
9
11
  "canonical_json": "{\"concurrency\":1,\"device_ids\":[\"DEVICE_001\"]}",
@@ -25,6 +27,55 @@
25
27
  },
26
28
  "canonical_json": "{\"concurrency\":1,\"device_filters\":[{\"brand\":\"Huawei\",\"count\":1,\"model\":\"P30\",\"os\":\"Android\",\"strategy\":\"weight\"}]}",
27
29
  "target_hash": "b57ee824993af6958955bdc74793c256efb47dcc26f4338dd2f265b3b9b7d901"
30
+ },
31
+ {
32
+ "name": "multiple_devices",
33
+ "target": {
34
+ "device_ids": [
35
+ "DEVICE_001",
36
+ "DEVICE_002"
37
+ ],
38
+ "concurrency": 1
39
+ },
40
+ "canonical_json": "{\"concurrency\":1,\"device_ids\":[\"DEVICE_001\",\"DEVICE_002\"]}",
41
+ "target_hash": "80870e64fa03e302a192ccfc933272a764ee1ec62f5d7fad517e70562d7d213a"
42
+ },
43
+ {
44
+ "name": "count_two",
45
+ "target": {
46
+ "device_filters": [
47
+ {
48
+ "os": "Android",
49
+ "count": 2,
50
+ "strategy": "random"
51
+ }
52
+ ],
53
+ "concurrency": 2
54
+ },
55
+ "canonical_json": "{\"concurrency\":2,\"device_filters\":[{\"count\":2,\"os\":\"Android\",\"strategy\":\"random\"}]}",
56
+ "target_hash": "42c254c3600ba7d4fedb10eb33110964149ed630f967c470a8c711532afdd6e1"
57
+ },
58
+ {
59
+ "name": "multiple_filters",
60
+ "target": {
61
+ "device_filters": [
62
+ {
63
+ "os": "Android",
64
+ "brand": "Huawei",
65
+ "count": 1,
66
+ "strategy": "weight"
67
+ },
68
+ {
69
+ "os": "Android",
70
+ "brand": "Samsung",
71
+ "count": 1,
72
+ "strategy": "weight"
73
+ }
74
+ ],
75
+ "concurrency": 2
76
+ },
77
+ "canonical_json": "{\"concurrency\":2,\"device_filters\":[{\"brand\":\"Huawei\",\"count\":1,\"os\":\"Android\",\"strategy\":\"weight\"},{\"brand\":\"Samsung\",\"count\":1,\"os\":\"Android\",\"strategy\":\"weight\"}]}",
78
+ "target_hash": "aa07c6fe0cba4ed2feda2982988278304820a0e8069d75bb2d9a27941ba0ccad"
28
79
  }
29
80
  ]
30
81
  }
@@ -14,5 +14,8 @@
14
14
  }
15
15
  ],
16
16
  "previewed_at": "2026-07-31T08:00:00Z",
17
- "reserved": false
17
+ "reserved": false,
18
+ "requested_count": 1,
19
+ "matched_count": 1,
20
+ "available_count": 1
18
21
  }
@@ -14,5 +14,8 @@
14
14
  }
15
15
  ],
16
16
  "previewed_at": "2026-07-31T08:00:00Z",
17
- "reserved": false
17
+ "reserved": false,
18
+ "requested_count": 1,
19
+ "matched_count": 1,
20
+ "available_count": 0
18
21
  }
@@ -0,0 +1,18 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://xagent.testin.cn/schemas/developer/v1/run-create-result.schema.json",
4
+ "title": "XAgent Run Create Result v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schema_version", "build_id", "runs"],
8
+ "properties": {
9
+ "schema_version": {"const": "xagent-run-create-result/v1"},
10
+ "build_id": {"type": "string", "pattern": "^bld_[a-f0-9]{32}$"},
11
+ "runs": {
12
+ "type": "array",
13
+ "minItems": 2,
14
+ "maxItems": 100,
15
+ "items": {"$ref": "run.schema.json"}
16
+ }
17
+ }
18
+ }