• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cartesi / rollups-explorer / 10497914455

21 Aug 2024 09:40PM UTC coverage: 93.39% (-0.4%) from 93.766%
10497914455

Pull #232

github

nevendyulgerov
test(apps/web): Add unit tests
Pull Request #232: #229 Add import and export for specifications

1210 of 1432 branches covered (84.5%)

Branch coverage included in aggregate %.

486 of 569 new or added lines in 8 files covered. (85.41%)

50 existing lines in 10 files now uncovered.

12863 of 13637 relevant lines covered (94.32%)

45.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.9
/apps/web/src/components/specification/transfer/validators/v1.ts
1
import {
1✔
2
    isArray,
1✔
3
    isNonEmptyString,
1✔
4
    isNotString,
1✔
5
    isNumber,
1✔
6
} from "ramda-adjunct";
1✔
7
import { parseAbi, parseAbiParameters } from "viem";
1✔
8
import {
1✔
9
    logicalOperators,
1✔
10
    operators,
1✔
11
    Specification,
1✔
12
    SPECIFICATION_TRANSFER_NAME,
1✔
13
    SpecificationTransfer,
1✔
14
} from "../../types";
1✔
15
import { formatAbi } from "abitype";
1✔
16
import { prepareSignatures } from "../../utils";
1✔
17
import { specModeValidation, specNameValidation } from "../../form/validations";
1✔
18
import { uniq } from "ramda";
1✔
19

1✔
20
const validateSpecifications = (
1✔
21
    specifications: Specification[],
17✔
22
): Promise<void> => {
17✔
23
    return new Promise((resolve, reject) => {
17✔
24
        const errors = uniq(
17✔
25
            specifications.reduce(
17✔
26
                (accumulator: (string | null)[], specification) => {
17✔
27
                    const nameError = specNameValidation(specification.name);
33✔
28
                    const modeError = specModeValidation(specification.mode);
33✔
29
                    const idError = isNonEmptyString(specification.id)
33✔
30
                        ? null
31✔
31
                        : "Id field with string value is required.";
2✔
32
                    const timestampError = isNumber(specification.timestamp)
33✔
33
                        ? null
31✔
34
                        : "Timestamp field with numeric value is required.";
2✔
35

33✔
36
                    let abiError = null;
33✔
37
                    if (specification.mode === "json_abi") {
33✔
38
                        const formattedAbi = formatAbi(
15✔
39
                            specification.abi ?? [],
15!
40
                        ).join("\n");
15✔
41
                        const items = prepareSignatures(formattedAbi);
15✔
42

15✔
43
                        try {
15✔
44
                            parseAbi(items);
15✔
45
                        } catch (error: any) {
15!
NEW
46
                            abiError = error.message;
×
NEW
47
                        }
×
48
                    } else if (specification.mode === "abi_params") {
33✔
49
                        try {
14✔
50
                            parseAbiParameters(specification.abiParams);
14✔
51
                        } catch (error: any) {
14✔
52
                            abiError = error.message;
1✔
53
                        }
1✔
54
                    }
14✔
55

32✔
56
                    const conditionsErrors = (
32✔
57
                        specification?.conditionals ?? []
33!
58
                    ).reduce(
33✔
59
                        (
33✔
60
                            conditionAccumulator: (string | null)[],
32✔
61
                            condition,
32✔
62
                        ) => {
32✔
63
                            const logicalOperatorError = logicalOperators.some(
32✔
64
                                (o) => o.value === condition.logicalOperator,
32✔
65
                            )
32✔
66
                                ? null
30✔
67
                                : `Invalid logical operator for condition. Valid values are: ${logicalOperators
2✔
68
                                      .map((o) => o.value)
2✔
69
                                      .join(", ")}`;
2✔
70

32✔
71
                            const schemaErrors = condition.conditions.reduce(
32✔
72
                                (
32✔
73
                                    conditionAccumulator: (string | null)[],
32✔
74
                                    conditionItem,
32✔
75
                                ) => {
32✔
76
                                    const error =
32✔
77
                                        isNonEmptyString(conditionItem.field) &&
32✔
78
                                        operators.some(
30✔
79
                                            (o) =>
30✔
80
                                                o.value ===
30✔
81
                                                conditionItem.operator,
30✔
82
                                        ) &&
30✔
83
                                        isNonEmptyString(conditionItem.value)
28✔
84
                                            ? null
26✔
85
                                            : "Invalid condition schema.";
6✔
86

32✔
87
                                    return [...conditionAccumulator, error];
32✔
88
                                },
32✔
89
                                [],
32✔
90
                            );
32✔
91

32✔
92
                            return [
32✔
93
                                ...conditionAccumulator,
32✔
94
                                logicalOperatorError,
32✔
95
                                ...schemaErrors,
32✔
96
                            ];
32✔
97
                        },
32✔
98
                        [],
33✔
99
                    );
33✔
100

33✔
101
                    let sliceErrors: (string | null)[] = [];
33✔
102
                    if (specification.mode === "abi_params") {
33✔
103
                        const sliceTargetError = (
14✔
104
                            specification?.sliceInstructions ?? []
14!
105
                        ).some((s) => s.name === specification.sliceTarget)
14✔
106
                            ? null
12✔
107
                            : "Invalid slice target.";
2✔
108

14✔
109
                        sliceErrors = [
14✔
110
                            ...(specification?.sliceInstructions ?? []).reduce(
14!
111
                                (
14✔
112
                                    sliceAccumulator: (string | null)[],
14✔
113
                                    sliceInstruction,
14✔
114
                                ) => {
14✔
115
                                    const error =
14✔
116
                                        isNumber(sliceInstruction.from) &&
14✔
117
                                        isNumber(sliceInstruction.to) &&
13✔
118
                                        isNonEmptyString(
12✔
119
                                            sliceInstruction.name,
12✔
120
                                        ) &&
12✔
121
                                        isNonEmptyString(sliceInstruction.type)
11✔
122
                                            ? null
10✔
123
                                            : "Invalid slice schema.";
4✔
124

14✔
125
                                    return [...sliceAccumulator, error];
14✔
126
                                },
14✔
127
                                [],
14✔
128
                            ),
14✔
129
                            sliceTargetError,
14✔
130
                        ];
14✔
131
                    }
14✔
132

32✔
133
                    const errors = [
32✔
134
                        nameError,
32✔
135
                        modeError,
32✔
136
                        idError,
32✔
137
                        timestampError,
32✔
138
                        abiError,
32✔
139
                        ...conditionsErrors,
32✔
140
                        ...sliceErrors,
32✔
141
                    ].filter((error) => isNonEmptyString(error));
32✔
142

32✔
143
                    return [...accumulator, ...errors];
32✔
144
                },
33✔
145
                [],
17✔
146
            ),
17✔
147
        );
17✔
148

17✔
149
        if (errors.length > 0) {
17✔
150
            return reject(errors);
15✔
151
        }
15✔
152

1✔
153
        resolve();
1✔
154
    });
17✔
155
};
17✔
156

1✔
157
const validateTopLevelSchema = (
1✔
158
    specificationImport: SpecificationTransfer,
20✔
159
): Promise<void> => {
20✔
160
    return new Promise((resolve, reject) => {
20✔
161
        if (
20✔
162
            isNotString(specificationImport.name) ||
20✔
163
            specificationImport.name !== SPECIFICATION_TRANSFER_NAME
20✔
164
        ) {
20✔
165
            return reject([
1✔
166
                `Missing or invalid 'name' field. The import must include a 'name' field with the string value of '${SPECIFICATION_TRANSFER_NAME}'.`,
1✔
167
            ]);
1✔
168
        }
1✔
169

19✔
170
        if (!isNumber(specificationImport.timestamp)) {
20✔
171
            return reject([
2✔
172
                "Missing 'timestamp' field. The import must include a 'timestamp' field with numeric value.",
2✔
173
            ]);
2✔
174
        }
2✔
175

17✔
176
        if (
17✔
177
            !isArray(specificationImport.specifications) ||
17✔
178
            specificationImport.specifications.length === 0
17✔
179
        ) {
20!
NEW
180
            return reject(["No specifications exist in the import."]);
×
NEW
181
        }
✔
182

17✔
183
        resolve();
17✔
184
    });
20✔
185
};
20✔
186

1✔
187
export const validateSpecificationImport = async (
1✔
188
    specificationImport: SpecificationTransfer,
20✔
189
): Promise<void> => {
20✔
190
    await validateTopLevelSchema(specificationImport);
20✔
191
    return await validateSpecifications(specificationImport.specifications);
17✔
192
};
17✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc