@oneuptime/common 12.0.0 → 12.0.2
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.
- package/Models/DatabaseModels/Host.ts +12 -4
- package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
- package/Models/DatabaseModels/KubernetesResource.ts +8 -7
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.ts +12 -2
- package/Models/DatabaseModels/PodmanResource.ts +6 -6
- package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
- package/Models/DatabaseModels/StatusPage.ts +95 -0
- package/Server/API/TeamMemberAPI.ts +85 -0
- package/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.ts +12 -2
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Middleware/ProjectAuthorization.ts +66 -4
- package/Server/Services/BillingService.ts +600 -51
- package/Server/Services/CephClusterService.ts +47 -5
- package/Server/Services/CephResourceService.ts +43 -4
- package/Server/Services/CloudResourceService.ts +47 -5
- package/Server/Services/DatabaseService.ts +87 -6
- package/Server/Services/DockerHostService.ts +47 -5
- package/Server/Services/DockerResourceService.ts +62 -8
- package/Server/Services/DockerSwarmClusterService.ts +48 -5
- package/Server/Services/DockerSwarmResourceService.ts +53 -4
- package/Server/Services/HostService.ts +48 -5
- package/Server/Services/IoTDeviceService.ts +1 -17
- package/Server/Services/IoTFleetService.ts +47 -5
- package/Server/Services/KubernetesClusterService.ts +48 -5
- package/Server/Services/KubernetesContainerService.ts +50 -9
- package/Server/Services/KubernetesResourceService.ts +53 -7
- package/Server/Services/LabelService.ts +106 -1
- package/Server/Services/LlmProviderService.ts +69 -9
- package/Server/Services/MonitorService.ts +57 -0
- package/Server/Services/PodmanHostService.ts +47 -5
- package/Server/Services/PodmanResourceService.ts +62 -8
- package/Server/Services/ProjectService.ts +165 -35
- package/Server/Services/ProxmoxClusterService.ts +47 -5
- package/Server/Services/ProxmoxResourceService.ts +43 -4
- package/Server/Services/RumApplicationService.ts +48 -5
- package/Server/Services/RunnerService.ts +1 -7
- package/Server/Services/ServerlessFunctionService.ts +48 -5
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
- package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
- package/Server/Services/StatusPageService.ts +95 -36
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
- package/Server/Services/StatusPageSubscriberService.ts +3 -0
- package/Server/Types/Database/JSONColumnQuery.ts +621 -0
- package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
- package/Server/Types/Database/QueryHelper.ts +17 -32
- package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +12 -0
- package/Tests/App/Dashboard/RunnerInstallInstructions.test.tsx +119 -0
- package/Tests/App/Dashboard/RunnerStatus.test.tsx +274 -0
- package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
- package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
- package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
- package/Tests/Server/Services/BillingService.test.ts +78 -3
- package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
- package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
- package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
- package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
- package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
- package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
- package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
- package/Tests/Server/Services/LlmProviderUsableByProject.test.ts +293 -0
- package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
- package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
- package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
- package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
- package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
- package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
- package/Tests/Server/Utils/AI/ToolArgsExtractors.test.ts +141 -0
- package/Tests/Server/Utils/AI/Toolbox/WidgetBuilder.test.ts +205 -0
- package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +897 -0
- package/Tests/Types/Events/Recurring.test.ts +157 -0
- package/Tests/Types/Runner/RunnerLiveStatus.test.ts +320 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +285 -0
- package/Tests/UI/Components/Detail/EntityFields.test.tsx +166 -0
- package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
- package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
- package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
- package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
- package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
- package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
- package/Types/CustomField/CustomFieldDefinition.ts +24 -0
- package/Types/Date.ts +112 -0
- package/Types/Events/Recurring.ts +99 -1
- package/Types/Runbook/RunbookStep.ts +15 -0
- package/Types/Runner/RunnerLiveStatus.ts +114 -0
- package/Types/StatusPage/StatusPageReport.ts +11 -0
- package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
- package/UI/Components/Detail/Detail.tsx +155 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
- package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
- package/UI/Utils/TeamMembersByUser.ts +302 -0
- package/Utils/StatusPage/ReportPeriod.ts +236 -0
- package/Utils/Telemetry/HostIpAddresses.ts +98 -0
- package/build/dist/Models/DatabaseModels/Host.js +12 -4
- package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
- package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
- package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js +16 -4
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLayer.js.map +1 -1
- package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
- package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/TeamMemberAPI.js +57 -2
- package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js +11 -1
- package/build/dist/Server/Infrastructure/Postgres/LocalMigrationGenerationDataSource.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +466 -36
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +42 -5
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CephResourceService.js +28 -3
- package/build/dist/Server/Services/CephResourceService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +42 -5
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +69 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +42 -5
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerResourceService.js +34 -4
- package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
- package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +43 -5
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +1 -8
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +42 -5
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
- package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
- package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +92 -0
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/LlmProviderService.js +61 -8
- package/build/dist/Server/Services/LlmProviderService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +47 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +42 -5
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/PodmanResourceService.js +34 -4
- package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +135 -21
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +43 -5
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -6
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +69 -46
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
- package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
- package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
- package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +11 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
- package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
- package/build/dist/Types/Date.js +68 -0
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +63 -0
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Runbook/RunbookStep.js.map +1 -1
- package/build/dist/Types/Runner/RunnerLiveStatus.js +69 -0
- package/build/dist/Types/Runner/RunnerLiveStatus.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
- package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +97 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
- package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
- package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
- package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
- package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +12 -1
|
@@ -0,0 +1,586 @@
|
|
|
1
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
2
|
+
import CephResource from "../../../Models/DatabaseModels/CephResource";
|
|
3
|
+
import DockerResource from "../../../Models/DatabaseModels/DockerResource";
|
|
4
|
+
import DockerSwarmResource from "../../../Models/DatabaseModels/DockerSwarmResource";
|
|
5
|
+
import IoTDevice from "../../../Models/DatabaseModels/IoTDevice";
|
|
6
|
+
import KubernetesContainer from "../../../Models/DatabaseModels/KubernetesContainer";
|
|
7
|
+
import KubernetesResource from "../../../Models/DatabaseModels/KubernetesResource";
|
|
8
|
+
import PodmanResource from "../../../Models/DatabaseModels/PodmanResource";
|
|
9
|
+
import ProxmoxResource from "../../../Models/DatabaseModels/ProxmoxResource";
|
|
10
|
+
import CephResourceService from "../../../Server/Services/CephResourceService";
|
|
11
|
+
import DockerResourceService from "../../../Server/Services/DockerResourceService";
|
|
12
|
+
import DockerSwarmResourceService from "../../../Server/Services/DockerSwarmResourceService";
|
|
13
|
+
import IoTDeviceService from "../../../Server/Services/IoTDeviceService";
|
|
14
|
+
import KubernetesContainerService from "../../../Server/Services/KubernetesContainerService";
|
|
15
|
+
import KubernetesResourceService from "../../../Server/Services/KubernetesResourceService";
|
|
16
|
+
import PodmanResourceService from "../../../Server/Services/PodmanResourceService";
|
|
17
|
+
import ProxmoxResourceService from "../../../Server/Services/ProxmoxResourceService";
|
|
18
|
+
import { getMaxLengthFromTableColumnType } from "../../../Types/Database/ColumnLength";
|
|
19
|
+
import { TableColumnMetadata } from "../../../Types/Database/TableColumn";
|
|
20
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
21
|
+
import { describe, expect, test } from "@jest/globals";
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* The contract that actually prevents issue #3006 from recurring:
|
|
25
|
+
* EVERY bounded varchar column an inventory service writes through its
|
|
26
|
+
* hand-built bulk INSERT must be clamped to the width the entity
|
|
27
|
+
* declares.
|
|
28
|
+
*
|
|
29
|
+
* Rather than hand-listing column positions (which rot the moment a
|
|
30
|
+
* column is added), each case here feeds a row whose every text field is
|
|
31
|
+
* absurdly oversized, then reads the INSERT's own column list out of the
|
|
32
|
+
* emitted SQL and looks each column's bound up in the entity metadata.
|
|
33
|
+
* Any bounded column whose bound parameter still exceeds it is a row
|
|
34
|
+
* that Postgres would reject with 22001 — aborting the whole 500-row
|
|
35
|
+
* chunk and silently dropping every other resource in the flush.
|
|
36
|
+
*
|
|
37
|
+
* This is what fails if someone widens a column without updating the
|
|
38
|
+
* sanitizer, narrows one without updating it, or adds a new bounded
|
|
39
|
+
* column to an INSERT and forgets to clamp it at all.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
const PROJECT_ID: ObjectID = ObjectID.generate();
|
|
43
|
+
const PARENT_ID: ObjectID = ObjectID.generate();
|
|
44
|
+
const OBSERVED_AT: Date = new Date("2026-08-05T00:00:00.000Z");
|
|
45
|
+
|
|
46
|
+
// Longer than the widest bounded varchar in the schema (500), by a lot.
|
|
47
|
+
const OVERSIZED: string = "x".repeat(4000);
|
|
48
|
+
|
|
49
|
+
interface RepositoryHolder {
|
|
50
|
+
getRepository: () => unknown;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ColumnAudit {
|
|
54
|
+
/** Bounded columns whose parameter is a string — the ones at risk. */
|
|
55
|
+
checked: Array<string>;
|
|
56
|
+
/** Bounded columns whose parameter would still blow the column up. */
|
|
57
|
+
violations: Array<string>;
|
|
58
|
+
/** Bounded columns clamped to exactly their declared bound. */
|
|
59
|
+
clampedToBound: Array<string>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface UpsertCase {
|
|
63
|
+
label: string;
|
|
64
|
+
service: RepositoryHolder;
|
|
65
|
+
model: BaseModel;
|
|
66
|
+
/** Bounded string columns the INSERT is expected to carry. */
|
|
67
|
+
boundedColumnCount: number;
|
|
68
|
+
/** jsonb columns the INSERT carries as a serialized payload. */
|
|
69
|
+
jsonPayloadColumnCount: number;
|
|
70
|
+
invoke: () => Promise<void>;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function mockQueryRunner(service: RepositoryHolder): jest.Mock {
|
|
74
|
+
const query: jest.Mock = jest.fn().mockResolvedValue([]);
|
|
75
|
+
jest
|
|
76
|
+
.spyOn(service, "getRepository")
|
|
77
|
+
.mockReturnValue({ manager: { query } } as any);
|
|
78
|
+
return query;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pull the column list straight out of `INSERT INTO "X" (...)`, so the
|
|
83
|
+
* audit follows the statement the service actually built rather than a
|
|
84
|
+
* copy of it maintained here.
|
|
85
|
+
*/
|
|
86
|
+
function insertColumnList(sql: string): Array<string> {
|
|
87
|
+
const match: RegExpMatchArray | null = sql.match(
|
|
88
|
+
/INSERT INTO\s+"[A-Za-z]+"\s*\(([^)]+)\)/,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (!match || !match[1]) {
|
|
92
|
+
throw new Error(`Could not parse an INSERT column list out of: ${sql}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return match[1]
|
|
96
|
+
.split(",")
|
|
97
|
+
.map((column: string) => {
|
|
98
|
+
return column.trim().replace(/^"|"$/g, "");
|
|
99
|
+
})
|
|
100
|
+
.filter((column: string) => {
|
|
101
|
+
return column.length > 0;
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Audit the first VALUES row of a bulk INSERT against the entity's
|
|
107
|
+
* declared column widths. Parameters are positional, so the first
|
|
108
|
+
* `columns.length` params are row one.
|
|
109
|
+
*/
|
|
110
|
+
function auditFirstRow(
|
|
111
|
+
model: BaseModel,
|
|
112
|
+
sql: string,
|
|
113
|
+
params: Array<unknown>,
|
|
114
|
+
): ColumnAudit {
|
|
115
|
+
const audit: ColumnAudit = {
|
|
116
|
+
checked: [],
|
|
117
|
+
violations: [],
|
|
118
|
+
clampedToBound: [],
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
insertColumnList(sql).forEach((column: string, index: number) => {
|
|
122
|
+
const metadata: TableColumnMetadata | undefined =
|
|
123
|
+
model.getTableColumnMetadata(column);
|
|
124
|
+
|
|
125
|
+
if (!metadata) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const maxLength: number | undefined = getMaxLengthFromTableColumnType(
|
|
130
|
+
metadata.type,
|
|
131
|
+
);
|
|
132
|
+
const value: unknown = params[index];
|
|
133
|
+
|
|
134
|
+
if (maxLength === undefined || typeof value !== "string") {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
audit.checked.push(column);
|
|
139
|
+
|
|
140
|
+
if (value.length > maxLength) {
|
|
141
|
+
audit.violations.push(
|
|
142
|
+
`"${column}" is varchar(${maxLength}) but the parameter carries ${value.length} characters`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (value.length === maxLength) {
|
|
147
|
+
audit.clampedToBound.push(column);
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return audit;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const CASES: Array<UpsertCase> = [
|
|
155
|
+
{
|
|
156
|
+
label: "KubernetesResourceService.bulkUpsert",
|
|
157
|
+
service: KubernetesResourceService,
|
|
158
|
+
model: new KubernetesResource(),
|
|
159
|
+
boundedColumnCount: 5,
|
|
160
|
+
jsonPayloadColumnCount: 1,
|
|
161
|
+
invoke: async (): Promise<void> => {
|
|
162
|
+
await KubernetesResourceService.bulkUpsert({
|
|
163
|
+
projectId: PROJECT_ID,
|
|
164
|
+
kubernetesClusterId: PARENT_ID,
|
|
165
|
+
resources: [
|
|
166
|
+
{
|
|
167
|
+
kind: OVERSIZED,
|
|
168
|
+
namespaceKey: OVERSIZED,
|
|
169
|
+
name: OVERSIZED,
|
|
170
|
+
uid: OVERSIZED,
|
|
171
|
+
phase: OVERSIZED,
|
|
172
|
+
isReady: true,
|
|
173
|
+
hasMemoryPressure: null,
|
|
174
|
+
hasDiskPressure: null,
|
|
175
|
+
hasPidPressure: null,
|
|
176
|
+
labels: { app: OVERSIZED },
|
|
177
|
+
annotations: null,
|
|
178
|
+
ownerReferences: null,
|
|
179
|
+
spec: null,
|
|
180
|
+
containerCount: 1,
|
|
181
|
+
status: null,
|
|
182
|
+
lastSeenAt: OBSERVED_AT,
|
|
183
|
+
resourceCreationTimestamp: null,
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
label: "KubernetesContainerService.bulkUpsert",
|
|
191
|
+
service: KubernetesContainerService,
|
|
192
|
+
model: new KubernetesContainer(),
|
|
193
|
+
boundedColumnCount: 6,
|
|
194
|
+
jsonPayloadColumnCount: 0,
|
|
195
|
+
invoke: async (): Promise<void> => {
|
|
196
|
+
await KubernetesContainerService.bulkUpsert({
|
|
197
|
+
projectId: PROJECT_ID,
|
|
198
|
+
kubernetesClusterId: PARENT_ID,
|
|
199
|
+
containers: [
|
|
200
|
+
{
|
|
201
|
+
podNamespaceKey: OVERSIZED,
|
|
202
|
+
podName: OVERSIZED,
|
|
203
|
+
name: OVERSIZED,
|
|
204
|
+
image: OVERSIZED,
|
|
205
|
+
state: OVERSIZED,
|
|
206
|
+
reason: OVERSIZED,
|
|
207
|
+
isReady: true,
|
|
208
|
+
restartCount: 0,
|
|
209
|
+
memoryLimitBytes: 1024,
|
|
210
|
+
lastSeenAt: OBSERVED_AT,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
label: "DockerResourceService.bulkUpsert",
|
|
218
|
+
service: DockerResourceService,
|
|
219
|
+
model: new DockerResource(),
|
|
220
|
+
boundedColumnCount: 5,
|
|
221
|
+
jsonPayloadColumnCount: 1,
|
|
222
|
+
invoke: async (): Promise<void> => {
|
|
223
|
+
await DockerResourceService.bulkUpsert({
|
|
224
|
+
projectId: PROJECT_ID,
|
|
225
|
+
dockerHostId: PARENT_ID,
|
|
226
|
+
resources: [
|
|
227
|
+
{
|
|
228
|
+
kind: OVERSIZED,
|
|
229
|
+
name: OVERSIZED,
|
|
230
|
+
containerId: OVERSIZED,
|
|
231
|
+
imageName: OVERSIZED,
|
|
232
|
+
state: OVERSIZED,
|
|
233
|
+
labels: { role: OVERSIZED },
|
|
234
|
+
resourceCreationTimestamp: null,
|
|
235
|
+
lastSeenAt: OBSERVED_AT,
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
label: "DockerResourceService.bulkUpsertContainers",
|
|
243
|
+
service: DockerResourceService,
|
|
244
|
+
model: new DockerResource(),
|
|
245
|
+
boundedColumnCount: 5,
|
|
246
|
+
jsonPayloadColumnCount: 0,
|
|
247
|
+
invoke: async (): Promise<void> => {
|
|
248
|
+
await DockerResourceService.bulkUpsertContainers({
|
|
249
|
+
projectId: PROJECT_ID,
|
|
250
|
+
dockerHostId: PARENT_ID,
|
|
251
|
+
containers: [
|
|
252
|
+
{
|
|
253
|
+
containerName: OVERSIZED,
|
|
254
|
+
containerId: OVERSIZED,
|
|
255
|
+
imageName: OVERSIZED,
|
|
256
|
+
state: OVERSIZED,
|
|
257
|
+
cpuPercent: 1,
|
|
258
|
+
memoryBytes: 1024,
|
|
259
|
+
observedAt: OBSERVED_AT,
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
});
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
label: "PodmanResourceService.bulkUpsert",
|
|
267
|
+
service: PodmanResourceService,
|
|
268
|
+
model: new PodmanResource(),
|
|
269
|
+
boundedColumnCount: 5,
|
|
270
|
+
jsonPayloadColumnCount: 1,
|
|
271
|
+
invoke: async (): Promise<void> => {
|
|
272
|
+
await PodmanResourceService.bulkUpsert({
|
|
273
|
+
projectId: PROJECT_ID,
|
|
274
|
+
podmanHostId: PARENT_ID,
|
|
275
|
+
resources: [
|
|
276
|
+
{
|
|
277
|
+
kind: OVERSIZED,
|
|
278
|
+
name: OVERSIZED,
|
|
279
|
+
containerId: OVERSIZED,
|
|
280
|
+
imageName: OVERSIZED,
|
|
281
|
+
state: OVERSIZED,
|
|
282
|
+
labels: { role: OVERSIZED },
|
|
283
|
+
resourceCreationTimestamp: null,
|
|
284
|
+
lastSeenAt: OBSERVED_AT,
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
label: "PodmanResourceService.bulkUpsertContainers",
|
|
292
|
+
service: PodmanResourceService,
|
|
293
|
+
model: new PodmanResource(),
|
|
294
|
+
boundedColumnCount: 5,
|
|
295
|
+
jsonPayloadColumnCount: 0,
|
|
296
|
+
invoke: async (): Promise<void> => {
|
|
297
|
+
await PodmanResourceService.bulkUpsertContainers({
|
|
298
|
+
projectId: PROJECT_ID,
|
|
299
|
+
podmanHostId: PARENT_ID,
|
|
300
|
+
containers: [
|
|
301
|
+
{
|
|
302
|
+
containerName: OVERSIZED,
|
|
303
|
+
containerId: OVERSIZED,
|
|
304
|
+
imageName: OVERSIZED,
|
|
305
|
+
state: OVERSIZED,
|
|
306
|
+
cpuPercent: 1,
|
|
307
|
+
memoryBytes: 1024,
|
|
308
|
+
observedAt: OBSERVED_AT,
|
|
309
|
+
},
|
|
310
|
+
],
|
|
311
|
+
});
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
label: "DockerSwarmResourceService.bulkUpsert",
|
|
316
|
+
service: DockerSwarmResourceService,
|
|
317
|
+
model: new DockerSwarmResource(),
|
|
318
|
+
boundedColumnCount: 11,
|
|
319
|
+
jsonPayloadColumnCount: 1,
|
|
320
|
+
invoke: async (): Promise<void> => {
|
|
321
|
+
await DockerSwarmResourceService.bulkUpsert({
|
|
322
|
+
projectId: PROJECT_ID,
|
|
323
|
+
dockerSwarmClusterId: PARENT_ID,
|
|
324
|
+
resources: [
|
|
325
|
+
{
|
|
326
|
+
kind: OVERSIZED,
|
|
327
|
+
externalId: OVERSIZED,
|
|
328
|
+
name: OVERSIZED,
|
|
329
|
+
state: OVERSIZED,
|
|
330
|
+
role: OVERSIZED,
|
|
331
|
+
serviceMode: OVERSIZED,
|
|
332
|
+
desiredReplicas: 1,
|
|
333
|
+
runningReplicas: 1,
|
|
334
|
+
image: OVERSIZED,
|
|
335
|
+
stackName: OVERSIZED,
|
|
336
|
+
serviceName: OVERSIZED,
|
|
337
|
+
nodeHostname: OVERSIZED,
|
|
338
|
+
driver: OVERSIZED,
|
|
339
|
+
isReady: true,
|
|
340
|
+
attributes: { note: OVERSIZED },
|
|
341
|
+
lastSeenAt: OBSERVED_AT,
|
|
342
|
+
},
|
|
343
|
+
],
|
|
344
|
+
});
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
label: "ProxmoxResourceService.bulkUpsert",
|
|
349
|
+
service: ProxmoxResourceService,
|
|
350
|
+
model: new ProxmoxResource(),
|
|
351
|
+
boundedColumnCount: 6,
|
|
352
|
+
jsonPayloadColumnCount: 0,
|
|
353
|
+
invoke: async (): Promise<void> => {
|
|
354
|
+
await ProxmoxResourceService.bulkUpsert({
|
|
355
|
+
projectId: PROJECT_ID,
|
|
356
|
+
proxmoxClusterId: PARENT_ID,
|
|
357
|
+
resources: [
|
|
358
|
+
{
|
|
359
|
+
kind: OVERSIZED,
|
|
360
|
+
externalId: OVERSIZED,
|
|
361
|
+
name: OVERSIZED,
|
|
362
|
+
vmid: 100,
|
|
363
|
+
guestType: OVERSIZED,
|
|
364
|
+
parentNodeName: OVERSIZED,
|
|
365
|
+
isUp: true,
|
|
366
|
+
haState: OVERSIZED,
|
|
367
|
+
onboot: null,
|
|
368
|
+
isBackedUp: null,
|
|
369
|
+
uptimeSeconds: 10,
|
|
370
|
+
lastSeenAt: OBSERVED_AT,
|
|
371
|
+
},
|
|
372
|
+
],
|
|
373
|
+
});
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
label: "CephResourceService.bulkUpsert",
|
|
378
|
+
service: CephResourceService,
|
|
379
|
+
model: new CephResource(),
|
|
380
|
+
boundedColumnCount: 6,
|
|
381
|
+
jsonPayloadColumnCount: 0,
|
|
382
|
+
invoke: async (): Promise<void> => {
|
|
383
|
+
await CephResourceService.bulkUpsert({
|
|
384
|
+
projectId: PROJECT_ID,
|
|
385
|
+
cephClusterId: PARENT_ID,
|
|
386
|
+
resources: [
|
|
387
|
+
{
|
|
388
|
+
kind: OVERSIZED,
|
|
389
|
+
externalId: OVERSIZED,
|
|
390
|
+
name: OVERSIZED,
|
|
391
|
+
hostname: OVERSIZED,
|
|
392
|
+
daemonVersion: OVERSIZED,
|
|
393
|
+
deviceClass: OVERSIZED,
|
|
394
|
+
isUp: true,
|
|
395
|
+
isIn: true,
|
|
396
|
+
inQuorum: null,
|
|
397
|
+
lastSeenAt: OBSERVED_AT,
|
|
398
|
+
},
|
|
399
|
+
],
|
|
400
|
+
});
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
label: "IoTDeviceService.bulkUpsert",
|
|
405
|
+
service: IoTDeviceService,
|
|
406
|
+
model: new IoTDevice(),
|
|
407
|
+
boundedColumnCount: 5,
|
|
408
|
+
jsonPayloadColumnCount: 0,
|
|
409
|
+
invoke: async (): Promise<void> => {
|
|
410
|
+
await IoTDeviceService.bulkUpsert({
|
|
411
|
+
projectId: PROJECT_ID,
|
|
412
|
+
iotFleetId: PARENT_ID,
|
|
413
|
+
devices: [
|
|
414
|
+
{
|
|
415
|
+
kind: OVERSIZED,
|
|
416
|
+
externalId: OVERSIZED,
|
|
417
|
+
name: OVERSIZED,
|
|
418
|
+
deviceType: OVERSIZED,
|
|
419
|
+
firmwareVersion: OVERSIZED,
|
|
420
|
+
isUp: true,
|
|
421
|
+
uptimeSeconds: 10,
|
|
422
|
+
lastSeenAt: OBSERVED_AT,
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
];
|
|
429
|
+
|
|
430
|
+
afterEach(() => {
|
|
431
|
+
jest.restoreAllMocks();
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
describe("every bulk INSERT fits the widths its entity declares", () => {
|
|
435
|
+
test.each(CASES)(
|
|
436
|
+
"$label clamps every bounded column",
|
|
437
|
+
async (upsertCase: UpsertCase) => {
|
|
438
|
+
const query: jest.Mock = mockQueryRunner(upsertCase.service);
|
|
439
|
+
await upsertCase.invoke();
|
|
440
|
+
|
|
441
|
+
expect(query).toHaveBeenCalledTimes(1);
|
|
442
|
+
const [sql, params] = query.mock.calls[0] as [string, Array<unknown>];
|
|
443
|
+
const audit: ColumnAudit = auditFirstRow(upsertCase.model, sql, params);
|
|
444
|
+
|
|
445
|
+
expect(audit.violations).toEqual([]);
|
|
446
|
+
},
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
test.each(CASES)(
|
|
450
|
+
"$label audits the bounded columns it is supposed to",
|
|
451
|
+
async (upsertCase: UpsertCase) => {
|
|
452
|
+
/*
|
|
453
|
+
* Guards the guard: without this, a regex that matched nothing
|
|
454
|
+
* would make the assertion above pass vacuously. Bump the count
|
|
455
|
+
* deliberately when a bounded column joins the INSERT — and clamp
|
|
456
|
+
* it in the service while you are there.
|
|
457
|
+
*/
|
|
458
|
+
const query: jest.Mock = mockQueryRunner(upsertCase.service);
|
|
459
|
+
await upsertCase.invoke();
|
|
460
|
+
|
|
461
|
+
const [sql, params] = query.mock.calls[0] as [string, Array<unknown>];
|
|
462
|
+
const audit: ColumnAudit = auditFirstRow(upsertCase.model, sql, params);
|
|
463
|
+
|
|
464
|
+
expect(audit.checked).toHaveLength(upsertCase.boundedColumnCount);
|
|
465
|
+
},
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
test.each(CASES)(
|
|
469
|
+
"$label actually clipped the oversized values, rather than dropping them",
|
|
470
|
+
async (upsertCase: UpsertCase) => {
|
|
471
|
+
const query: jest.Mock = mockQueryRunner(upsertCase.service);
|
|
472
|
+
await upsertCase.invoke();
|
|
473
|
+
|
|
474
|
+
const [sql, params] = query.mock.calls[0] as [string, Array<unknown>];
|
|
475
|
+
const audit: ColumnAudit = auditFirstRow(upsertCase.model, sql, params);
|
|
476
|
+
|
|
477
|
+
/*
|
|
478
|
+
* Every input was oversized, so every bounded column that came
|
|
479
|
+
* from the input must now sit exactly on its bound.
|
|
480
|
+
*/
|
|
481
|
+
expect(audit.clampedToBound.length).toBeGreaterThan(0);
|
|
482
|
+
expect(params).not.toContain(OVERSIZED);
|
|
483
|
+
},
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
test.each(CASES)(
|
|
487
|
+
"$label leaves its jsonb payloads unclamped",
|
|
488
|
+
async (upsertCase: UpsertCase) => {
|
|
489
|
+
/*
|
|
490
|
+
* jsonb payloads (labels / annotations / attributes / status) have
|
|
491
|
+
* no varchar bound, so clamping them would corrupt real data for
|
|
492
|
+
* no reason.
|
|
493
|
+
*
|
|
494
|
+
* jsonPayloadColumnCount is asserted explicitly because without it
|
|
495
|
+
* this case executes ZERO assertions for the services that carry no
|
|
496
|
+
* jsonb column — and "no assertions ran" reads exactly like
|
|
497
|
+
* "everything checked out".
|
|
498
|
+
*/
|
|
499
|
+
const query: jest.Mock = mockQueryRunner(upsertCase.service);
|
|
500
|
+
await upsertCase.invoke();
|
|
501
|
+
|
|
502
|
+
const [sql, params] = query.mock.calls[0] as [string, Array<unknown>];
|
|
503
|
+
const columns: Array<string> = insertColumnList(sql);
|
|
504
|
+
const jsonPayloadColumns: Array<string> = [];
|
|
505
|
+
|
|
506
|
+
columns.forEach((column: string, index: number) => {
|
|
507
|
+
const metadata: TableColumnMetadata | undefined =
|
|
508
|
+
upsertCase.model.getTableColumnMetadata(column);
|
|
509
|
+
const value: unknown = params[index];
|
|
510
|
+
|
|
511
|
+
if (
|
|
512
|
+
!metadata ||
|
|
513
|
+
typeof value !== "string" ||
|
|
514
|
+
getMaxLengthFromTableColumnType(metadata.type) !== undefined
|
|
515
|
+
) {
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/*
|
|
520
|
+
* Unbounded and string-valued: a serialized JSON blob. It must
|
|
521
|
+
* still carry the full oversized payload we fed in.
|
|
522
|
+
*/
|
|
523
|
+
if (value.startsWith("{")) {
|
|
524
|
+
jsonPayloadColumns.push(column);
|
|
525
|
+
expect(value).toContain(OVERSIZED);
|
|
526
|
+
expect(value.length).toBeGreaterThan(OVERSIZED.length);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
expect(jsonPayloadColumns).toHaveLength(
|
|
531
|
+
upsertCase.jsonPayloadColumnCount,
|
|
532
|
+
);
|
|
533
|
+
},
|
|
534
|
+
);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
describe("the parity audit itself", () => {
|
|
538
|
+
test("flags a column whose parameter exceeds the declared width", () => {
|
|
539
|
+
/*
|
|
540
|
+
* A test for the test: prove the sweep above is capable of failing.
|
|
541
|
+
* KubernetesResource."kind" is varchar(100).
|
|
542
|
+
*/
|
|
543
|
+
const audit: ColumnAudit = auditFirstRow(
|
|
544
|
+
new KubernetesResource(),
|
|
545
|
+
'INSERT INTO "KubernetesResource" ("kind", "name") VALUES ($1, $2)',
|
|
546
|
+
[OVERSIZED, "api"],
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
expect(audit.violations).toHaveLength(1);
|
|
550
|
+
expect(audit.violations[0]).toContain('"kind"');
|
|
551
|
+
expect(audit.violations[0]).toContain("varchar(100)");
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
test("accepts a column sitting exactly on its declared width", () => {
|
|
555
|
+
const audit: ColumnAudit = auditFirstRow(
|
|
556
|
+
new KubernetesResource(),
|
|
557
|
+
'INSERT INTO "KubernetesResource" ("kind", "name") VALUES ($1, $2)',
|
|
558
|
+
["k".repeat(100), "n".repeat(500)],
|
|
559
|
+
);
|
|
560
|
+
|
|
561
|
+
expect(audit.violations).toEqual([]);
|
|
562
|
+
expect(audit.checked).toEqual(["kind", "name"]);
|
|
563
|
+
expect(audit.clampedToBound).toEqual(["kind", "name"]);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
test("reads the widened name bound off KubernetesResource, not a constant", () => {
|
|
567
|
+
/*
|
|
568
|
+
* 500 here comes from the entity; if the model regressed to
|
|
569
|
+
* ShortText this would flag a violation instead.
|
|
570
|
+
*/
|
|
571
|
+
const audit: ColumnAudit = auditFirstRow(
|
|
572
|
+
new KubernetesResource(),
|
|
573
|
+
'INSERT INTO "KubernetesResource" ("name") VALUES ($1)',
|
|
574
|
+
["n".repeat(500)],
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
expect(audit.violations).toEqual([]);
|
|
578
|
+
expect(audit.clampedToBound).toEqual(["name"]);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test("throws rather than passing vacuously when it cannot find a column list", () => {
|
|
582
|
+
expect(() => {
|
|
583
|
+
return auditFirstRow(new KubernetesResource(), "UPDATE something", []);
|
|
584
|
+
}).toThrow("Could not parse an INSERT column list");
|
|
585
|
+
});
|
|
586
|
+
});
|