diff --git a/DEPS.bzl b/DEPS.bzl index fbb2d1f4ad7d..a1d34133745a 100644 --- a/DEPS.bzl +++ b/DEPS.bzl @@ -1431,10 +1431,10 @@ def go_deps(): patches = [ "@com_github_cockroachdb_cockroach//build/patches:com_github_cockroachdb_pebble.patch", ], - sha256 = "1d37b28ca579f0d052ae51133945a47626dec63ceef0f07262aaedb033da1d25", - strip_prefix = "github.com/cockroachdb/pebble@v0.0.0-20220706210916-d68c152233af", + sha256 = "ba44b4818846bee833b98b11e22efa4c04efed9533ba16e8e7013a52a5cbc1ab", + strip_prefix = "github.com/cockroachdb/pebble@v0.0.0-20220718171027-6b9be031039a", urls = [ - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/pebble/com_github_cockroachdb_pebble-v0.0.0-20220706210916-d68c152233af.zip", + "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/pebble/com_github_cockroachdb_pebble-v0.0.0-20220718171027-6b9be031039a.zip", ], ) go_repository( diff --git a/build/bazelutil/distdir_files.bzl b/build/bazelutil/distdir_files.bzl index c0295c934796..ac523b62447c 100644 --- a/build/bazelutil/distdir_files.bzl +++ b/build/bazelutil/distdir_files.bzl @@ -187,7 +187,7 @@ DISTDIR_FILES = { "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/go-test-teamcity/com_github_cockroachdb_go_test_teamcity-v0.0.0-20191211140407-cff980ad0a55.zip": "bac30148e525b79d004da84d16453ddd2d5cd20528e9187f1d7dac708335674b", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/gostdlib/com_github_cockroachdb_gostdlib-v1.13.0.zip": "b3d43d8f95edf65f73a5348f29e1159823cac64b148f8d3bb48340bf55d70872", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/logtags/com_github_cockroachdb_logtags-v0.0.0-20211118104740-dabe8e521a4f.zip": "1972c3f171f118add3fd9e64bcea6cbb9959a3b7fa0ada308e8a7310813fea74", - "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/pebble/com_github_cockroachdb_pebble-v0.0.0-20220706210916-d68c152233af.zip": "1d37b28ca579f0d052ae51133945a47626dec63ceef0f07262aaedb033da1d25", + "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/pebble/com_github_cockroachdb_pebble-v0.0.0-20220718171027-6b9be031039a.zip": "ba44b4818846bee833b98b11e22efa4c04efed9533ba16e8e7013a52a5cbc1ab", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/redact/com_github_cockroachdb_redact-v1.1.3.zip": "7778b1e4485e4f17f35e5e592d87eb99c29e173ac9507801d000ad76dd0c261e", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/returncheck/com_github_cockroachdb_returncheck-v0.0.0-20200612231554-92cdbca611dd.zip": "ce92ba4352deec995b1f2eecf16eba7f5d51f5aa245a1c362dfe24c83d31f82b", "https://storage.googleapis.com/cockroach-godeps/gomod/github.com/cockroachdb/sentry-go/com_github_cockroachdb_sentry_go-v0.6.1-cockroachdb.2.zip": "fbb2207d02aecfdd411b1357efe1192dbb827959e36b7cab7491731ac55935c9", diff --git a/go.mod b/go.mod index c7e9c1a52371..22fcf49aa4e2 100644 --- a/go.mod +++ b/go.mod @@ -47,7 +47,7 @@ require ( github.com/cockroachdb/go-test-teamcity v0.0.0-20191211140407-cff980ad0a55 github.com/cockroachdb/gostdlib v1.13.0 github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f - github.com/cockroachdb/pebble v0.0.0-20220706210916-d68c152233af + github.com/cockroachdb/pebble v0.0.0-20220718171027-6b9be031039a github.com/cockroachdb/redact v1.1.3 github.com/cockroachdb/returncheck v0.0.0-20200612231554-92cdbca611dd github.com/cockroachdb/stress v0.0.0-20220310203902-58fb4627376e diff --git a/go.sum b/go.sum index 4bc99a180a8e..082f625f33ee 100644 --- a/go.sum +++ b/go.sum @@ -468,8 +468,8 @@ github.com/cockroachdb/gostdlib v1.13.0/go.mod h1:eXX95p9QDrYwJfJ6AgeN9QnRa/lqqi github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/pebble v0.0.0-20220706210916-d68c152233af h1:N+92y/jD6W4An7/SHHscbSpBru3dg5AB5xQ+XwG6o9Y= -github.com/cockroachdb/pebble v0.0.0-20220706210916-d68c152233af/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4= +github.com/cockroachdb/pebble v0.0.0-20220718171027-6b9be031039a h1:zZ4qaemk1HqFFlQsYBFW88JljJirycMZq2tiG3UP3AE= +github.com/cockroachdb/pebble v0.0.0-20220718171027-6b9be031039a/go.mod h1:pr479tNxFRmcfDyklTqoRMDDVmRlEbL+d7a7rhKnrI4= github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= diff --git a/pkg/ccl/sqlproxyccl/proxy_handler.go b/pkg/ccl/sqlproxyccl/proxy_handler.go index 8c913363ee32..9ab70c4242f4 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler.go @@ -43,10 +43,14 @@ var ( // See "options" in https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS. clusterIdentifierLongOptionRE = regexp.MustCompile(`(?:-c\s*|--)cluster=([\S]*)`) - // clusterNameRegex restricts cluster names to have between 6 and 20 - // alphanumeric characters, with dashes allowed within the name (but not as a - // starting or ending character). - clusterNameRegex = regexp.MustCompile("^[a-z0-9][a-z0-9-]{4,18}[a-z0-9]$") + // clusterNameRegex restricts cluster names to have between 6 and 100 + // alphanumeric characters, with dashes allowed within the name (but not as + // a starting or ending character). + // + // Note that the limit for cluster names within CockroachCloud is likely + // smaller than 100 characters. We don't perform an exact match here for + // more flexibility. + clusterNameRegex = regexp.MustCompile("^[a-z0-9][a-z0-9-]{4,98}[a-z0-9]$") ) const ( diff --git a/pkg/ccl/sqlproxyccl/proxy_handler_test.go b/pkg/ccl/sqlproxyccl/proxy_handler_test.go index 67245a1fea82..656adea1bf0b 100644 --- a/pkg/ccl/sqlproxyccl/proxy_handler_test.go +++ b/pkg/ccl/sqlproxyccl/proxy_handler_test.go @@ -1482,7 +1482,7 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { { name: "invalid cluster identifier in database param", params: map[string]string{ - // Cluster names need to be between 6 to 20 alphanumeric characters. + // Cluster names need to be between 6 to 100 alphanumeric characters. "database": "short-0.defaultdb", }, expectedError: "invalid cluster identifier 'short-0'", @@ -1491,11 +1491,18 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { { name: "invalid cluster identifier in options param", params: map[string]string{ - // Cluster names need to be between 6 to 20 alphanumeric characters. - "options": "--cluster=cockroachlabsdotcomfoobarbaz-0", + // Cluster names need to be between 6 to 100 alphanumeric characters. + "options": fmt.Sprintf("--cluster=%s-0", strings.Repeat("a", 101)), }, - expectedError: "invalid cluster identifier 'cockroachlabsdotcomfoobarbaz-0'", - expectedHint: "Is 'cockroachlabsdotcomfoobarbaz' a valid cluster name?\n--\n" + clusterNameFormHint, + expectedError: fmt.Sprintf( + "invalid cluster identifier '%s-0'", + strings.Repeat("a", 101), + ), + expectedHint: fmt.Sprintf( + "Is '%s' a valid cluster name?\n--\n%s", + strings.Repeat("a", 101), + clusterNameFormHint, + ), }, { name: "invalid database param (1)", @@ -1576,10 +1583,10 @@ func TestClusterNameAndTenantFromParams(t *testing.T) { { name: "cluster identifier in database param", params: map[string]string{ - "database": "happy-koala-7.defaultdb", + "database": fmt.Sprintf("%s-7.defaultdb", strings.Repeat("a", 100)), "foo": "bar", }, - expectedClusterName: "happy-koala", + expectedClusterName: strings.Repeat("a", 100), expectedTenantID: 7, expectedParams: map[string]string{"database": "defaultdb", "foo": "bar"}, }, diff --git a/pkg/ui/workspaces/db-console/package.json b/pkg/ui/workspaces/db-console/package.json index 541c6e9d7da6..bbb921b20a58 100644 --- a/pkg/ui/workspaces/db-console/package.json +++ b/pkg/ui/workspaces/db-console/package.json @@ -15,7 +15,7 @@ "cypress:run": "yarn cypress run --browser chrome", "cypress:run-visual-regression": "yarn cypress run --browser chrome --spec 'cypress/integration/**/*.visual.spec.ts'", "cypress:update-snapshots": "yarn cypress run --env updateSnapshots=true --spec 'cypress/integration/**/*.visual.spec.ts'", - "test": "DEBUG_PRINT_LIMIT=100000 jest" + "test": "DEBUG_PRINT_LIMIT=1000000 jest" }, "dependencies": { "@babel/runtime": "7.12.13", diff --git a/pkg/ui/workspaces/db-console/src/app.spec.tsx b/pkg/ui/workspaces/db-console/src/app.spec.tsx index 72ffbd11074e..5405976c2608 100644 --- a/pkg/ui/workspaces/db-console/src/app.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/app.spec.tsx @@ -27,114 +27,93 @@ stubComponentInModule("src/views/transactions/transactionsPage", "default"); import React from "react"; import { Action, Store } from "redux"; import { createMemoryHistory, MemoryHistory } from "history"; -import { mount } from "enzyme"; +import { screen, render } from "@testing-library/react"; import { App } from "src/app"; import { AdminUIState, createAdminUIStore } from "src/redux/state"; -import ClusterOverview from "src/views/cluster/containers/clusterOverview"; -import NodeList from "src/views/clusterviz/containers/map/nodeList"; -import { ClusterVisualization } from "src/views/clusterviz/containers/map"; -import { NodeGraphs } from "src/views/cluster/containers/nodeGraphs"; -import { NodeOverview } from "src/views/cluster/containers/nodeOverview"; -import { Logs } from "src/views/cluster/containers/nodeLogs"; -import { EventPageUnconnected } from "src/views/cluster/containers/events"; -import { DatabasesPage } from "src/views/databases/databasesPage"; -import { DatabaseDetailsPage } from "src/views/databases/databaseDetailsPage"; -import { DatabaseTablePage } from "src/views/databases/databaseTablePage"; -import DataDistributionPageConnected from "src/views/cluster/containers/dataDistribution"; -import SQLActivityPage from "src/views/sqlActivity/sqlActivityPage"; -import StatementsPage from "src/views/statements/statementsPage"; -import TransactionsPage from "src/views/transactions/transactionsPage"; -import { - JobsPage, - StatementDetails, - TransactionDetails, - ActiveStatementDetails, - ActiveTransactionDetails, -} from "@cockroachlabs/cluster-ui"; -import Debug from "src/views/reports/containers/debug"; -import { ReduxDebug } from "src/views/reports/containers/redux"; -import { CustomChart } from "src/views/reports/containers/customChart"; -import { EnqueueRange } from "src/views/reports/containers/enqueueRange"; -import { RangesMain } from "src/views/devtools/containers/raftRanges"; -import { RaftMessages } from "src/views/devtools/containers/raftMessages"; -import Raft from "src/views/devtools/containers/raft"; -import NotFound from "src/views/app/components/errorMessage/notFound"; -import { ProblemRanges } from "src/views/reports/containers/problemRanges"; -import { Localities } from "src/views/reports/containers/localities"; -import { Nodes } from "src/views/reports/containers/nodes"; -import { DecommissionedNodeHistory } from "src/views/reports"; -import { Network } from "src/views/reports/containers/network"; -import { Settings } from "src/views/reports/containers/settings"; -import { Certificates } from "src/views/reports/containers/certificates"; -import { Range } from "src/views/reports/containers/range"; -import { Stores } from "src/views/reports/containers/stores"; +const CLUSTER_OVERVIEW_CAPACITY_LABEL = "Capacity Usage"; +const CLUSTER_VIZ_NODE_MAP_LABEL = "Node Map"; +const METRICS_HEADER = "Metrics"; +const NODE_LIST_LABEL = /Nodes \([\d]\)/; +const LOADING_CLUSTER_STATUS = /Loading cluster status.*/; +const NODE_LOG_HEADER = /Logs Node.*/; +const EVENTS_HEADER = "Events"; +const JOBS_HEADER = "Jobs"; +const DATABASES_HEADER = "Databases"; +const SQL_ACTIVITY_HEADER = "SQL Activity"; +const STATEMENTS_DETAILS_HEADER = "Statement Fingerprint"; +const TRANSACTION_DETAILS_HEADER = "Transaction Details"; +const STATEMENT_EXECUTION_HEADER = /Statement Execution ID:.*/; +const TRANSACTION_EXECUTION_HEADER = /Transaction Execution ID:.*/; +const ADVANCED_DEBUG_HEADER = "Advanced Debugging"; +const REDUX_DEBUG_HEADER = "Redux State"; +const CUSTOM_METRICS_CHART_HEADER = "Custom Chart"; +const ENQUEUE_RANGE_HEADER = "Manually enqueue range in a replica queue"; +const RAFT_HEADER = "Raft"; +const RAFT_MESSAGES_HEADER = "Pending Heartbeats"; +const PROBLEM_RANGES_HEADER = "Problem Ranges Report"; +const LOCALITIES_REPORT_HEADER = "Localities"; +const NODE_DIAGNOSTICS_REPORT_HEADER = "Node Diagnostics"; +const DECOMMISSIONED_HISTORY_REPORT = "Decommissioned Node History"; +const NETWORK_DIAGNOSTICS_REPORT_HEADER = "Network Diagnostics"; +const CLUSTER_SETTINGS_REPORT = "Cluster Settings"; +const CERTIFICATES_REPORT_HEADER = "Certificates"; +const RANGE_REPORT_HEADER = /Range Report for.*/; +const STORES_REPORT_HEADER = "Stores"; +const UNKNOWN_PAGE_LABEL = /We can.*t find the page.*/; describe("Routing to", () => { let history: MemoryHistory; - let appWrapper: ReturnType; beforeEach(() => { history = createMemoryHistory({ initialEntries: ["/"], }); const store: Store = createAdminUIStore(history); - appWrapper = mount(); - }); - - afterEach(() => { - appWrapper.unmount(); + render(); }); const navigateToPath = (path: string) => { history.push(path); - appWrapper.update(); }; describe("'/' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/"); - expect(appWrapper.find(ClusterOverview).length).toBe(1); + screen.getByText(CLUSTER_OVERVIEW_CAPACITY_LABEL); }); - it("redirected to '/overview'", () => { + test("redirected to '/overview'", () => { navigateToPath("/"); - const location = history.location; - expect(location.pathname).toEqual("/overview/list"); + expect(history.location.pathname).toBe("/overview/list"); }); }); describe("'/overview' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/overview"); - expect(appWrapper.find(ClusterOverview).length).toBe(1); + screen.getByText(CLUSTER_OVERVIEW_CAPACITY_LABEL); }); - it("redirected to '/overview'", () => { + test("redirected to '/overview'", () => { navigateToPath("/overview"); - const location = history.location; - expect(location.pathname).toEqual("/overview/list"); + expect(history.location.pathname).toBe("/overview/list"); }); }); describe("'/overview/list' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/overview"); - const clusterOverview = appWrapper.find(ClusterOverview); - expect(clusterOverview.length).toBe(1); - const nodeList = clusterOverview.find(NodeList); - expect(nodeList.length).toBe(1); + screen.getByText(CLUSTER_OVERVIEW_CAPACITY_LABEL); + screen.getByText(NODE_LIST_LABEL); }); }); describe("'/overview/map' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/overview/map"); - const clusterOverview = appWrapper.find(ClusterOverview); - const clusterViz = appWrapper.find(ClusterVisualization); - expect(clusterOverview.length).toBe(1); - expect(clusterViz.length).toBe(1); + expect(screen.getAllByText(CLUSTER_VIZ_NODE_MAP_LABEL).length).toBe(2); }); }); @@ -142,69 +121,66 @@ describe("Routing to", () => { /* time series metrics */ } describe("'/metrics' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); - it("redirected to '/metrics/overview/cluster'", () => { + test("redirected to '/metrics/overview/cluster'", () => { navigateToPath("/metrics"); - const location = history.location; - expect(location.pathname).toEqual("/metrics/overview/cluster"); + expect(history.location.pathname).toBe("/metrics/overview/cluster"); }); }); describe("'/metrics/overview/cluster' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/overview/cluster"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); }); describe("'/metrics/overview/node' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/overview/node"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); }); describe("'/metrics/:dashboardNameAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/some-dashboard"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); - it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { + test("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { navigateToPath("/metrics/some-dashboard"); - const location = history.location; - expect(location.pathname).toEqual("/metrics/some-dashboard/cluster"); + expect(history.location.pathname).toBe("/metrics/some-dashboard/cluster"); }); }); describe("'/metrics/:dashboardNameAttr/cluster' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/some-dashboard/cluster"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); }); describe("'/metrics/:dashboardNameAttr/node' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/some-dashboard/node"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); - it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { + test("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { navigateToPath("/metrics/some-dashboard/node"); - const location = history.location; - expect(location.pathname).toEqual("/metrics/some-dashboard/cluster"); + expect(history.location.pathname).toBe("/metrics/some-dashboard/cluster"); }); }); describe("'/metrics/:dashboardNameAttr/node/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/metrics/some-dashboard/node/123"); - expect(appWrapper.find(NodeGraphs).length).toBe(1); + screen.getByText(METRICS_HEADER, { selector: "h3" }); }); }); @@ -212,29 +188,28 @@ describe("Routing to", () => { /* node details */ } describe("'/node' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/node"); - expect(appWrapper.find(NodeList).length).toBe(1); + screen.getByText(NODE_LIST_LABEL); }); - it("redirected to '/overview/list'", () => { + test("redirected to '/overview/list'", () => { navigateToPath("/node"); - const location = history.location; - expect(location.pathname).toEqual("/overview/list"); + expect(history.location.pathname).toBe("/overview/list"); }); }); describe("'/node/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/node/1"); - expect(appWrapper.find(NodeOverview).length).toBe(1); + screen.getByText(LOADING_CLUSTER_STATUS, { selector: "h1" }); }); }); describe("'/node/:nodeIDAttr/logs' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/node/1/logs"); - expect(appWrapper.find(Logs).length).toBe(1); + screen.getByText(NODE_LOG_HEADER, { selector: "h2" }); }); }); @@ -242,16 +217,16 @@ describe("Routing to", () => { /* events & jobs */ } describe("'/events' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/events"); - expect(appWrapper.find(EventPageUnconnected).length).toBe(1); + screen.getByText(EVENTS_HEADER, { selector: "h1" }); }); }); describe("'/jobs' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/jobs"); - expect(appWrapper.find(JobsPage).length).toBe(1); + screen.getByText(JOBS_HEADER, { selector: "h3" }); }); }); @@ -259,65 +234,60 @@ describe("Routing to", () => { /* databases */ } describe("'/databases' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/databases"); - expect(appWrapper.find(DatabasesPage).length).toBe(1); + screen.getByText(DATABASES_HEADER, { selector: "h3" }); }); }); describe("'/databases/tables' path", () => { - it("redirected to '/databases'", () => { + test("redirected to '/databases'", () => { navigateToPath("/databases/tables"); - const location = history.location; - expect(location.pathname).toEqual("/databases"); + expect(history.location.pathname).toBe("/databases"); }); }); describe("'/databases/grants' path", () => { - it("redirected to '/databases'", () => { + test("redirected to '/databases'", () => { navigateToPath("/databases/grants"); - const location = history.location; - expect(location.pathname).toEqual("/databases"); + expect(history.location.pathname).toBe("/databases"); }); }); describe("'/databases/database/:${databaseNameAttr}/table/:${tableNameAttr}' path", () => { - it("redirected to '/database/:${databaseNameAttr}/table/:${tableNameAttr}'", () => { + test("redirected to '/database/:${databaseNameAttr}/table/:${tableNameAttr}'", () => { navigateToPath("/databases/database/some-db-name/table/some-table-name"); - const location = history.location; - expect(location.pathname).toEqual( + expect(history.location.pathname).toBe( "/database/some-db-name/table/some-table-name", ); }); }); describe("'/database' path", () => { - it("redirected to '/databases'", () => { + test("redirected to '/databases'", () => { navigateToPath("/database"); - const location = history.location; - expect(location.pathname).toEqual("/databases"); + expect(history.location.pathname).toBe("/databases"); }); }); describe("'/database/:${databaseNameAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/database/some-db-name"); - expect(appWrapper.find(DatabaseDetailsPage).length).toBe(1); + screen.getByTestId("DatabaseDetailsPage"); }); }); describe("'/database/:${databaseNameAttr}/table' path", () => { - it("redirected to '/databases/:${databaseNameAttr}'", () => { + test("redirected to '/databases/:${databaseNameAttr}'", () => { navigateToPath("/database/some-db-name/table"); - const location = history.location; - expect(location.pathname).toEqual("/database/some-db-name"); + expect(history.location.pathname).toBe("/database/some-db-name"); }); }); describe("'/database/:${databaseNameAttr}/table/:${tableNameAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/database/some-db-name/table/some-table-name"); - expect(appWrapper.find(DatabaseTablePage).length).toBe(1); + screen.getByTestId("DatabaseTablePage"); }); }); @@ -325,9 +295,9 @@ describe("Routing to", () => { /* data distribution */ } describe("'/data-distribution' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/data-distribution"); - expect(appWrapper.find(DataDistributionPageConnected).length).toBe(1); + screen.getByTestId("dataDistribution"); }); }); @@ -335,64 +305,65 @@ describe("Routing to", () => { /* statement statistics */ } describe("'/statements' path", () => { - it("redirects to '/sql-activity' statement tab", () => { + test("redirects to '/sql-activity' statement tab", () => { navigateToPath("/statements"); - expect(appWrapper.find(SQLActivityPage).length).toBe(1); + screen.getByText(SQL_ACTIVITY_HEADER, { selector: "h3" }); + screen.getByRole("tab", { name: "Statements", selected: true }); }); }); describe("'/statements/:${appAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/statements/%24+internal"); - expect(appWrapper.find(StatementsPage).length).toBe(1); + screen.getByText(SQL_ACTIVITY_HEADER, { selector: "h3" }); + screen.getByRole("tab", { name: "Statements", selected: true }); }); }); describe("'/statements/:${appAttr}/:${statementAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/statements/%24+internal/true"); - expect(appWrapper.find(StatementDetails).length).toBe(1); + screen.getByText(STATEMENTS_DETAILS_HEADER, { selector: "h3" }); }); }); describe("'/statements/:${implicitTxnAttr}/:${statementAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/statements/implicit-txn-attr/statement-attr"); - expect(appWrapper.find(StatementDetails).length).toBe(1); + screen.getByText(STATEMENTS_DETAILS_HEADER, { selector: "h3" }); }); }); describe("'/statement' path", () => { - it("redirected to '/sql-activity?tab=Statements&view=fingerprints'", () => { + test("redirected to '/sql-activity?tab=Statements&view=fingerprints'", () => { navigateToPath("/statement"); - const location = history.location; - expect(location.pathname + location.search).toEqual( + expect(history.location.pathname + history.location.search).toBe( "/sql-activity?tab=Statements&view=fingerprints", ); }); }); describe("'/statement/:${implicitTxnAttr}/:${statementAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/statement/implicit-attr/statement-attr/"); - expect(appWrapper.find(StatementDetails).length).toBe(1); + screen.getByText(STATEMENTS_DETAILS_HEADER, { selector: "h3" }); }); }); describe("'/sql-activity?tab=Statements' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/sql-activity?tab=Statements"); - expect(appWrapper.find(StatementsPage).length).toBe(1); + screen.getByRole("tab", { name: "Statements", selected: true }); }); - it("routes to component with view=fingerprints", () => { + test("routes to component with view=fingerprints", () => { navigateToPath("/sql-activity?tab=Statements&view=fingerprints"); - expect(appWrapper.find(StatementsPage).length).toBe(1); + screen.getByRole("tab", { name: "Statements", selected: true }); }); - it("routes to component with view=active", () => { + test("routes to component with view=active", () => { navigateToPath("/sql-activity?tab=Statements&view=active"); - expect(appWrapper.find(StatementsPage).length).toBe(1); + screen.getByRole("tab", { name: "Statements", selected: true }); }); }); @@ -400,72 +371,72 @@ describe("Routing to", () => { /* transactions statistics */ } describe("'/sql-activity?tab=Transactions' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/sql-activity?tab=Transactions"); - expect(appWrapper.find(TransactionsPage).length).toBe(1); + screen.getByRole("tab", { name: "Transactions", selected: true }); }); - it("routes to component with view=fingerprints", () => { + test("routes to component with view=fingerprints", () => { navigateToPath("/sql-activity?tab=Transactions&view=fingerprints"); - expect(appWrapper.find(TransactionsPage).length).toBe(1); + screen.getByRole("tab", { name: "Transactions", selected: true }); }); - it("routes to component with view=active", () => { + test("routes to component with view=active", () => { navigateToPath("/sql-activity?tab=Transactions&view=active"); - expect(appWrapper.find(TransactionsPage).length).toBe(1); + screen.getByRole("tab", { name: "Transactions", selected: true }); }); }); describe("'/transaction/:aggregated_ts/:txn_fingerprint_id' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/transaction/1637877600/4948941983164833719"); - expect(appWrapper.find(TransactionDetails).length).toBe(1); + screen.getByText(TRANSACTION_DETAILS_HEADER, { selector: "h3" }); }); }); // Active execution details. describe("'/execution' path", () => { - it("'/execution/statement/statementID' routes to ", () => { + test("'/execution/statement/statementID' routes to ", () => { navigateToPath("/execution/statement/stmtID"); - expect(appWrapper.find(ActiveStatementDetails).length).toBe(1); + screen.getByText(STATEMENT_EXECUTION_HEADER, { selector: "h3" }); }); - it("'/execution/transaction/transactionID' routes to ", () => { + test("'/execution/transaction/transactionID' routes to ", () => { navigateToPath("/execution/transaction/transactionID"); - expect(appWrapper.find(ActiveTransactionDetails).length).toBe(1); + screen.getByText(TRANSACTION_EXECUTION_HEADER, { selector: "h3" }); }); }); { /* debug pages */ } describe("'/debug' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/debug"); - expect(appWrapper.find(Debug).length).toBe(1); + screen.getByText(ADVANCED_DEBUG_HEADER); }); }); - // TODO (koorosh): Disabled due to strange failure on internal - // behavior of ReduxDebug component under test env. - xdescribe("'/debug/redux' path", () => { - it("routes to component", () => { + describe("'/debug/redux' path", () => { + test("routes to component", () => { navigateToPath("/debug/redux"); - expect(appWrapper.find(ReduxDebug).length).toBe(1); + screen.getByText(REDUX_DEBUG_HEADER); }); }); describe("'/debug/chart' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/debug/chart"); - expect(appWrapper.find(CustomChart).length).toBe(1); + screen.getByText(CUSTOM_METRICS_CHART_HEADER); }); }); describe("'/debug/enqueue_range' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/debug/enqueue_range"); - expect(appWrapper.find(EnqueueRange).length).toBe(1); + screen.getByText(ENQUEUE_RANGE_HEADER, { + selector: "h1", + }); }); }); @@ -473,113 +444,113 @@ describe("Routing to", () => { /* raft pages */ } describe("'/raft' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/raft"); - expect(appWrapper.find(Raft).length).toBe(1); + screen.getByText(RAFT_HEADER); }); - it("redirected to '/raft/ranges'", () => { + test("redirected to '/raft/ranges'", () => { navigateToPath("/raft"); - const location = history.location; - expect(location.pathname).toEqual("/raft/ranges"); + expect(history.location.pathname).toBe("/raft/ranges"); }); }); describe("'/raft/ranges' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/raft/ranges"); - expect(appWrapper.find(RangesMain).length).toBe(1); + screen.getByText(RAFT_HEADER); }); }); describe("'/raft/messages/all' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/raft/messages/all"); - expect(appWrapper.find(RaftMessages).length).toBe(1); + screen.getByText(RAFT_MESSAGES_HEADER); }); }); describe("'/raft/messages/node/:${nodeIDAttr}' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/raft/messages/node/node-id-attr"); - expect(appWrapper.find(RaftMessages).length).toBe(1); + screen.getByText(RAFT_MESSAGES_HEADER); }); }); describe("'/reports/problemranges' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/problemranges"); - expect(appWrapper.find(ProblemRanges).length).toBe(1); + screen.getByText(PROBLEM_RANGES_HEADER); }); }); describe("'/reports/problemranges/:nodeIDAttr' path", () => { it("routes to component", () => { navigateToPath("/reports/problemranges/1"); - expect(appWrapper.find(ProblemRanges).length).toBe(1); + navigateToPath("/reports/problemranges"); + screen.getByText(PROBLEM_RANGES_HEADER); }); }); describe("'/reports/localities' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/localities"); - expect(appWrapper.find(Localities).length).toBe(1); + screen.getByText(LOCALITIES_REPORT_HEADER); }); }); describe("'/reports/nodes' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/nodes"); - expect(appWrapper.find(Nodes).length).toBe(1); + screen.getByText(NODE_DIAGNOSTICS_REPORT_HEADER); }); }); describe("'/reports/nodes/history' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/nodes/history"); - expect(appWrapper.find(DecommissionedNodeHistory).length).toBe(1); + screen.getByText(DECOMMISSIONED_HISTORY_REPORT); }); }); describe("'/reports/network' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/network"); - expect(appWrapper.find(Network).length).toBe(1); + screen.getByText(NETWORK_DIAGNOSTICS_REPORT_HEADER); }); }); describe("'/reports/network/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/network/1"); - expect(appWrapper.find(Network).length).toBe(1); + screen.getByText(NETWORK_DIAGNOSTICS_REPORT_HEADER); }); }); describe("'/reports/settings' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/settings"); - expect(appWrapper.find(Settings).length).toBe(1); + screen.getByText(CLUSTER_SETTINGS_REPORT); }); }); describe("'/reports/certificates/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/certificates/1"); - expect(appWrapper.find(Certificates).length).toBe(1); + screen.getByText(CERTIFICATES_REPORT_HEADER); }); }); describe("'/reports/range/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/range/1"); - expect(appWrapper.find(Range).length).toBe(1); + screen.getByText(RANGE_REPORT_HEADER); }); }); describe("'/reports/stores/:nodeIDAttr' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/reports/stores/1"); - expect(appWrapper.find(Stores).length).toBe(1); + screen.getByText(STORES_REPORT_HEADER); }); }); @@ -587,110 +558,99 @@ describe("Routing to", () => { /* old route redirects */ } describe("'/cluster' path", () => { - it("redirected to '/metrics/overview/cluster'", () => { + test("redirected to '/metrics/overview/cluster'", () => { navigateToPath("/cluster"); - const location = history.location; - expect(location.pathname).toEqual("/metrics/overview/cluster"); + expect(history.location.pathname).toBe("/metrics/overview/cluster"); }); }); describe("'/cluster/all/:${dashboardNameAttr}' path", () => { - it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { + test("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { const dashboardNameAttr = "some-dashboard-name"; navigateToPath(`/cluster/all/${dashboardNameAttr}`); - const location = history.location; - expect(location.pathname).toEqual( + expect(history.location.pathname).toBe( `/metrics/${dashboardNameAttr}/cluster`, ); }); }); describe("'/cluster/node/:${nodeIDAttr}/:${dashboardNameAttr}' path", () => { - it("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { + test("redirected to '/metrics/:${dashboardNameAttr}/cluster'", () => { const dashboardNameAttr = "some-dashboard-name"; const nodeIDAttr = 1; navigateToPath(`/cluster/node/${nodeIDAttr}/${dashboardNameAttr}`); - const location = history.location; - expect(location.pathname).toEqual( + expect(history.location.pathname).toBe( `/metrics/${dashboardNameAttr}/node/${nodeIDAttr}`, ); }); }); describe("'/cluster/nodes' path", () => { - it("redirected to '/overview/list'", () => { + test("redirected to '/overview/list'", () => { navigateToPath("/cluster/nodes"); - const location = history.location; - expect(location.pathname).toEqual("/overview/list"); + expect(history.location.pathname).toBe("/overview/list"); }); }); describe("'/cluster/nodes/:${nodeIDAttr}' path", () => { - it("redirected to '/node/:${nodeIDAttr}'", () => { + test("redirected to '/node/:${nodeIDAttr}'", () => { const nodeIDAttr = 1; navigateToPath(`/cluster/nodes/${nodeIDAttr}`); - const location = history.location; - expect(location.pathname).toEqual(`/node/${nodeIDAttr}`); + expect(history.location.pathname).toBe(`/node/${nodeIDAttr}`); }); }); describe("'/cluster/nodes/:${nodeIDAttr}/logs' path", () => { - it("redirected to '/node/:${nodeIDAttr}/logs'", () => { + test("redirected to '/node/:${nodeIDAttr}/logs'", () => { const nodeIDAttr = 1; navigateToPath(`/cluster/nodes/${nodeIDAttr}/logs`); - const location = history.location; - expect(location.pathname).toEqual(`/node/${nodeIDAttr}/logs`); + expect(history.location.pathname).toBe(`/node/${nodeIDAttr}/logs`); }); }); describe("'/cluster/events' path", () => { - it("redirected to '/events'", () => { + test("redirected to '/events'", () => { navigateToPath("/cluster/events"); - const location = history.location; - expect(location.pathname).toEqual("/events"); + expect(history.location.pathname).toBe("/events"); }); }); describe("'/cluster/nodes' path", () => { - it("redirected to '/overview/list'", () => { + test("redirected to '/overview/list'", () => { navigateToPath("/cluster/nodes"); - const location = history.location; - expect(location.pathname).toEqual("/overview/list"); + expect(history.location.pathname).toBe("/overview/list"); }); }); describe("'/unknown-url' path", () => { - it("routes to component", () => { + test("routes to component", () => { navigateToPath("/some-random-ulr"); - expect(appWrapper.find(NotFound).length).toBe(1); + screen.getByText(UNKNOWN_PAGE_LABEL); }); }); describe("'/statements' path", () => { - it("redirected to '/sql-activity?tab=Statements&view=fingerprints'", () => { + test("redirected to '/sql-activity?tab=Statements&view=fingerprints'", () => { navigateToPath("/statements"); - const location = history.location; - expect(location.pathname + location.search).toEqual( + expect(history.location.pathname + history.location.search).toBe( "/sql-activity?tab=Statements&view=fingerprints", ); }); }); describe("'/sessions' path", () => { - it("redirected to '/sql-activity?tab=Sessions'", () => { + test("redirected to '/sql-activity?tab=Sessions'", () => { navigateToPath("/sessions"); - const location = history.location; - expect(location.pathname + location.search).toEqual( + expect(history.location.pathname + history.location.search).toBe( "/sql-activity?tab=Sessions", ); }); }); describe("'/transactions' path", () => { - it("redirected to '/sql-activity?tab=Transactions'", () => { + test("redirected to '/sql-activity?tab=Transactions'", () => { navigateToPath("/transactions"); - const location = history.location; - expect(location.pathname + location.search).toEqual( + expect(history.location.pathname + history.location.search).toBe( "/sql-activity?tab=Transactions", ); }); diff --git a/pkg/ui/workspaces/db-console/src/setupTests.js b/pkg/ui/workspaces/db-console/src/setupTests.js index 0565e2ced2fe..84ba7631da2d 100644 --- a/pkg/ui/workspaces/db-console/src/setupTests.js +++ b/pkg/ui/workspaces/db-console/src/setupTests.js @@ -29,7 +29,6 @@ import Adapter from "enzyme-adapter-react-16"; // } //TODO(barag): does this need to all be in a beforeAll()/afterAll so we can restore all of these mocks? - Enzyme.configure({ adapter: new Adapter() }); /** @@ -63,9 +62,3 @@ afterAll(() => { delete window.matchMedia; delete window.fetch; }); - -// afterAll(() => { -// console.log("[setupTests::afterAll] writing v8 heap snapshot…"); -// const dst = v8.writeHeapSnapshot(); -// console.log("[setupTests::afterAll] done. see", dst); -// }); diff --git a/pkg/ui/workspaces/db-console/src/test-utils/mockComponent.tsx b/pkg/ui/workspaces/db-console/src/test-utils/mockComponent.tsx index a34616824caa..9f802f363620 100644 --- a/pkg/ui/workspaces/db-console/src/test-utils/mockComponent.tsx +++ b/pkg/ui/workspaces/db-console/src/test-utils/mockComponent.tsx @@ -41,7 +41,7 @@ export function stubComponentInModule( if (typeof candidate === "function") { const componentName = name === "default" ? path.split("/").pop() : name; mocks[name] = (props: Record) => - (
) as any; + (
) as any; } else { throw new Error( `Unable to mock '${path}' property '${name}', which has type '${typeof candidate}'`, diff --git a/pkg/ui/workspaces/db-console/src/views/app/components/errorMessage/errorBoundary.tsx b/pkg/ui/workspaces/db-console/src/views/app/components/errorMessage/errorBoundary.tsx index 7d8e0234a420..3ebfcf5d089a 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/components/errorMessage/errorBoundary.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/components/errorMessage/errorBoundary.tsx @@ -40,7 +40,7 @@ export default class ErrorBoundary extends React.Component< } componentDidCatch(error: Error, errorInfo: ErrorInfo) { - // Console.error for developer visibility. + // Console.error for developer visibility as well as production logging. console.error("[ErrorBoundary::componentDidCatch] error = ", error); console.error("[ErrorBoundary::componentDidCatch] errorInfo = ", errorInfo); console.log("children = ", this.props.children); diff --git a/pkg/ui/workspaces/db-console/yarn.lock b/pkg/ui/workspaces/db-console/yarn.lock index cece7a20e03c..e8221b75a3ea 100644 --- a/pkg/ui/workspaces/db-console/yarn.lock +++ b/pkg/ui/workspaces/db-console/yarn.lock @@ -4552,32 +4552,6 @@ babel-preset-jest@^27.5.1: babel-plugin-jest-hoist "^27.5.1" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^27.5.1: - version "27.5.1" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz#91f10f58034cb7989cb4f962b69fa6eef6a6bc81" - integrity sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag== - dependencies: - babel-plugin-jest-hoist "^27.5.1" - babel-preset-current-node-syntax "^1.0.0" - babel-runtime@6.x, babel-runtime@^6.0.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" @@ -5122,18 +5096,6 @@ capture-exit@^2.0.0: dependencies: rsvp "^4.8.4" -caniuse-lite@^1.0.30001332: - version "1.0.30001344" - resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz" - integrity sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g== - -capture-exit@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" - integrity sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g== - dependencies: - rsvp "^4.8.4" - case-sensitive-paths-webpack-plugin@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" diff --git a/vendor b/vendor index 3487db885988..422a67489ca4 160000 --- a/vendor +++ b/vendor @@ -1 +1 @@ -Subproject commit 3487db885988ea0ee06e33d872f44782aacb2229 +Subproject commit 422a67489ca47b7ca2c6aedfbc3cb8a7586d0df0