mirror of
https://github.com/morluto/rea.git
synced 2026-11-10 17:30:32 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
909e34c416 |
No files matched your search
@@ -50,6 +50,41 @@ it.each([
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ component: "example.app" }, ["component"]],
|
||||
[{ component: "example.app/" }, ["component"]],
|
||||
[
|
||||
{ extras: [{ key: "count", type: "int", value: "2147483648" }] },
|
||||
["extras", 0, "value"],
|
||||
],
|
||||
[
|
||||
{ extras: [{ key: "count", type: "int", value: "-2147483649" }] },
|
||||
["extras", 0, "value"],
|
||||
],
|
||||
[
|
||||
{ extras: [{ key: "count", type: "long", value: "9223372036854775808" }] },
|
||||
["extras", 0, "value"],
|
||||
],
|
||||
[
|
||||
{ extras: [{ key: "count", type: "long", value: "-9223372036854775809" }] },
|
||||
["extras", 0, "value"],
|
||||
],
|
||||
] as const)(
|
||||
"rejects an unrepresentable activity argument before any effect: %j",
|
||||
async (input, path) => {
|
||||
const service = new AdbDeviceAnalysisService(refusedPort);
|
||||
const result = await service.execute("start_adb_activity", {
|
||||
serial: "fixture",
|
||||
action: "example.TEST",
|
||||
...input,
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
ok: false,
|
||||
error: { _tag: "AnalysisInputError", issues: [{ path }] },
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects an unknown operation before any effect", async () => {
|
||||
const service = new AdbDeviceAnalysisService(refusedPort);
|
||||
const result = await service.execute("unknown_adb_operation" as never, {});
|
||||
|
||||
@@ -2,11 +2,11 @@ import { expect, it } from "vitest";
|
||||
|
||||
import { adbInputSchemas } from "./adbDeviceAnalysis.js";
|
||||
|
||||
it("preserves ADB key, action and component admission when making advertised patterns portable", () => {
|
||||
it("matches portable patterns for ADB keys, actions and complete components", () => {
|
||||
const serial = "emulator-5554";
|
||||
const variants = [
|
||||
{
|
||||
previous: /^[A-Za-z0-9._-]+$/u,
|
||||
expectedPattern: /^[A-Za-z0-9._-]+$/u,
|
||||
max: 256,
|
||||
accepts: (key: string) =>
|
||||
adbInputSchemas.read_adb_setting.safeParse({
|
||||
@@ -16,7 +16,7 @@ it("preserves ADB key, action and component admission when making advertised pat
|
||||
}).success,
|
||||
},
|
||||
{
|
||||
previous: /^[A-Za-z0-9._-]+$/u,
|
||||
expectedPattern: /^[A-Za-z0-9._-]+$/u,
|
||||
max: 256,
|
||||
accepts: (key: string) =>
|
||||
adbInputSchemas.start_adb_activity.safeParse({
|
||||
@@ -26,14 +26,14 @@ it("preserves ADB key, action and component admission when making advertised pat
|
||||
}).success,
|
||||
},
|
||||
{
|
||||
previous: /^[A-Za-z][A-Za-z0-9._-]*$/u,
|
||||
expectedPattern: /^[A-Za-z][A-Za-z0-9._-]*$/u,
|
||||
max: 256,
|
||||
accepts: (action: string) =>
|
||||
adbInputSchemas.start_adb_activity.safeParse({ serial, action })
|
||||
.success,
|
||||
},
|
||||
{
|
||||
previous: /^[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._$-]*)?$/u,
|
||||
expectedPattern: /^[A-Za-z0-9._-]+\/[A-Za-z0-9._$-]+$/u,
|
||||
max: 512,
|
||||
accepts: (component: string) =>
|
||||
adbInputSchemas.start_adb_activity.safeParse({
|
||||
@@ -53,6 +53,7 @@ it("preserves ADB key, action and component admission when making advertised pat
|
||||
"android.intent.action.VIEW",
|
||||
"com.example/.MainActivity",
|
||||
"com.example/.Outer$InnerActivity",
|
||||
"com.example/com.example.Outer$InnerActivity",
|
||||
"com.example.Outer$InnerActivity",
|
||||
"com.example/",
|
||||
"com.example/a/b",
|
||||
@@ -75,11 +76,11 @@ it("preserves ADB key, action and component admission when making advertised pat
|
||||
for (const sample of samples)
|
||||
expect(
|
||||
variant.accepts(sample),
|
||||
JSON.stringify({ pattern: variant.previous.source, sample }),
|
||||
JSON.stringify({ pattern: variant.expectedPattern.source, sample }),
|
||||
).toBe(
|
||||
sample.length > 0 &&
|
||||
sample.length <= variant.max &&
|
||||
variant.previous.test(sample),
|
||||
variant.expectedPattern.test(sample),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -97,6 +98,9 @@ it("admits empty string extras and validates typed extra text at domain admissio
|
||||
const accepted = [
|
||||
{ type: "boolean", value: "false" },
|
||||
{ type: "int", value: "-2147483648" },
|
||||
{ type: "int", value: "0002147483647" },
|
||||
{ type: "long", value: "-0009223372036854775808" },
|
||||
{ type: "long", value: "9223372036854775807" },
|
||||
{ type: "long", value: "9007199254740993" },
|
||||
{ type: "float", value: "-1.25" },
|
||||
{ type: "float", value: "1e-3" },
|
||||
@@ -106,14 +110,13 @@ it("admits empty string extras and validates typed extra text at domain admissio
|
||||
{ type: "float", value: "0x1.8p1" },
|
||||
{ type: "float", value: "NaN" },
|
||||
] as const;
|
||||
for (const { type, value } of accepted)
|
||||
for (const { type, value } of accepted) {
|
||||
const extras = [{ key: "value", type, value }];
|
||||
expect(
|
||||
adbInputSchemas.start_adb_activity.safeParse({
|
||||
...input,
|
||||
extras: [{ key: "value", type, value }],
|
||||
}).success,
|
||||
adbInputSchemas.start_adb_activity.parse({ ...input, extras }).extras,
|
||||
`${type}: ${value}`,
|
||||
).toBe(true);
|
||||
).toEqual(extras);
|
||||
}
|
||||
const rejected = [
|
||||
{ type: "boolean", value: "True" },
|
||||
{ type: "int", value: "1.5" },
|
||||
|
||||
@@ -54,11 +54,29 @@ const keyPattern = /^[A-Za-z0-9._\x2d]+$/u;
|
||||
const activityExtraKeySchema = keyStringSchema
|
||||
.regex(keyPattern, "Extra keys are identifiers")
|
||||
.describe("Extra key");
|
||||
const MAX_ADB_ACTIVITY_EXTRA_VALUE_LENGTH = 2048;
|
||||
const activityExtraStringValueSchema = z
|
||||
.string()
|
||||
.max(2048)
|
||||
.max(MAX_ADB_ACTIVITY_EXTRA_VALUE_LENGTH)
|
||||
.regex(/^[^\x00]*$/u, "Extra values cannot contain NUL");
|
||||
const activityExtraNonEmptyValueSchema = activityExtraStringValueSchema.min(1);
|
||||
/** Check Java int/long range without rounding or changing the caller's text. */
|
||||
const javaDecimalExtraValueSchema = (bits: 32 | 64) => {
|
||||
const upperBound = 1n << BigInt(bits - 1);
|
||||
const range = `signed ${String(bits)}-bit`;
|
||||
return z
|
||||
.string()
|
||||
.max(MAX_ADB_ACTIVITY_EXTRA_VALUE_LENGTH, { abort: true })
|
||||
.regex(/^-?\d+$/u, {
|
||||
message: "Integer extra values must be signed decimal text",
|
||||
abort: true,
|
||||
})
|
||||
.refine((value) => {
|
||||
const decimal = BigInt(value);
|
||||
return decimal >= -upperBound && decimal < upperBound;
|
||||
}, `Integer extra value is outside the ${range} range`)
|
||||
.describe(`Integer extra value as ${range} decimal text`);
|
||||
};
|
||||
/** Maximum typed extras admitted for one Android activity request. */
|
||||
export const MAX_ADB_ACTIVITY_EXTRAS = 16;
|
||||
// Android's `am start --ef` parses this text with Float.valueOf. Keep the
|
||||
@@ -83,16 +101,12 @@ const activityExtraSchema = z.discriminatedUnion("type", [
|
||||
z.strictObject({
|
||||
key: activityExtraKeySchema,
|
||||
type: z.literal("int").describe("Integer extra value type"),
|
||||
value: activityExtraNonEmptyValueSchema
|
||||
.regex(/^-?\d+$/u, "Integer extra values must be an integer")
|
||||
.describe("Integer extra value as signed decimal text"),
|
||||
value: javaDecimalExtraValueSchema(32),
|
||||
}),
|
||||
z.strictObject({
|
||||
key: activityExtraKeySchema,
|
||||
type: z.literal("long").describe("Long extra value type"),
|
||||
value: activityExtraNonEmptyValueSchema
|
||||
.regex(/^-?\d+$/u, "Long extra values must be an integer")
|
||||
.describe("Long extra value as signed decimal text"),
|
||||
value: javaDecimalExtraValueSchema(64),
|
||||
}),
|
||||
z.strictObject({
|
||||
key: activityExtraKeySchema,
|
||||
@@ -330,7 +344,7 @@ export const adbInputSchemas = {
|
||||
.min(1)
|
||||
.max(512)
|
||||
.regex(
|
||||
/^[A-Za-z0-9._\x2d]+(?:\/[A-Za-z0-9._$\x2d]*)?$/u,
|
||||
/^[A-Za-z0-9._\x2d]+\/[A-Za-z0-9._$\x2d]+$/u,
|
||||
"Component must be package/activity form",
|
||||
)
|
||||
.optional()
|
||||
|
||||
Reference in new issue
Block a user