From 8fc321aca3c0be5ecbff42b303095861bca7e755 Mon Sep 17 00:00:00 2001
From: Ariya Hidayat <ariya@metabase.com>
Date: Thu, 4 Nov 2021 12:35:09 -0700
Subject: [PATCH] CI with GitHub Action: run tokenizer fuzzing tests (#18841)

---
 .github/workflows/fuzzing.yml                 | 43 +++++++++++++++++++
 .../expressions/fuzz.tokenizer.unit.spec.js   | 27 ++++++++++++
 .../lib/expressions/tokenizer.unit.spec.js    | 19 --------
 3 files changed, 70 insertions(+), 19 deletions(-)
 create mode 100644 .github/workflows/fuzzing.yml
 create mode 100644 frontend/test/metabase/lib/expressions/fuzz.tokenizer.unit.spec.js

diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml
new file mode 100644
index 00000000000..64ff87ab6b8
--- /dev/null
+++ b/.github/workflows/fuzzing.yml
@@ -0,0 +1,43 @@
+name: Fuzzing
+
+on:
+  push:
+    branches:
+      - '**'
+    paths:
+    - 'frontend/**'
+    - 'shared/**'
+    - 'enterprise/frontend/**'
+    - 'docs/**'
+    - '**/package.json'
+    - '**/yarn.lock'
+    - '**/.eslintrc'
+    - '.github/workflows/**'
+  pull_request:
+
+jobs:
+
+  fe-fuzz-tokenizer:
+    runs-on: ubuntu-20.04
+    timeout-minutes: 7
+    steps:
+    - uses: actions/checkout@v2
+    - name: Prepare Node.js
+      uses: actions/setup-node@v1
+      with:
+        node-version: 14.x
+    - name: Get M2 cache
+      uses: actions/cache@v2
+      with:
+        path: ~/.m2
+        key: ${{ runner.os }}-cljs-${{ hashFiles('**/shadow-cljs.edn') }}
+    - name: Get yarn cache
+      uses: actions/cache@v2
+      with:
+        path: ~/.cache/yarn
+        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
+    - run: yarn install --frozen-lockfile --prefer-offline
+    - run: yarn test-unit frontend/test/metabase/lib/expressions/fuzz.tokenizer.unit.spec.js
+      env:
+        MB_FUZZ: 1
+      name: Run fuzz testing on the tokenizer
diff --git a/frontend/test/metabase/lib/expressions/fuzz.tokenizer.unit.spec.js b/frontend/test/metabase/lib/expressions/fuzz.tokenizer.unit.spec.js
new file mode 100644
index 00000000000..1867aa885c5
--- /dev/null
+++ b/frontend/test/metabase/lib/expressions/fuzz.tokenizer.unit.spec.js
@@ -0,0 +1,27 @@
+import { tokenize } from "metabase/lib/expressions/tokenizer";
+
+import { generateExpression } from "./generator";
+
+const fuzz = process.env.MB_FUZZ ? describe : describe.skip;
+
+describe("metabase/lib/expressions/tokenizer", () => {
+  // quick sanity check before the real fuzzing
+  it("should tokenize custom expresssion", () => {
+    expect(() => tokenize("CASE([Deal],[Price]*7e-1,[Price]")).not.toThrow();
+  });
+});
+
+fuzz("FUZZING metabase/lib/expressions/tokenizer", () => {
+  const MAX_SEED = 5e4;
+
+  for (let seed = 0; seed < MAX_SEED; ++seed) {
+    it("should handle generated expression from seed " + seed, () => {
+      const { expression } = generateExpression(seed);
+      expect(() => tokenize(expression)).not.toThrow();
+    });
+    it("should not error on generated expression from seed " + seed, () => {
+      const { expression } = generateExpression(seed);
+      expect(tokenize(expression).errors).toEqual([]);
+    });
+  }
+});
diff --git a/frontend/test/metabase/lib/expressions/tokenizer.unit.spec.js b/frontend/test/metabase/lib/expressions/tokenizer.unit.spec.js
index dad7655c5ee..91a2163cbe8 100644
--- a/frontend/test/metabase/lib/expressions/tokenizer.unit.spec.js
+++ b/frontend/test/metabase/lib/expressions/tokenizer.unit.spec.js
@@ -4,8 +4,6 @@ import {
   OPERATOR as OP,
 } from "metabase/lib/expressions/tokenizer";
 
-import { generateExpression } from "./generator";
-
 describe("metabase/lib/expressions/tokenizer", () => {
   const types = expr => tokenize(expr).tokens.map(t => t.type);
   const ops = expr => tokenize(expr).tokens.map(t => t.op);
@@ -142,20 +140,3 @@ describe("metabase/lib/expressions/tokenizer", () => {
     expect(errors("    #")[0].pos).toEqual(4);
   });
 });
-
-if (process.env.MB_FUZZ) {
-  describe("FUZZING metabase/lib/expressions/tokenizer", () => {
-    const MAX_SEED = 5e4;
-
-    for (let seed = 0; seed < MAX_SEED; ++seed) {
-      it("should handle generated expression from seed " + seed, () => {
-        const { expression } = generateExpression(seed);
-        expect(() => tokenize(expression)).not.toThrow();
-      });
-      it("should not error on generated expression from seed " + seed, () => {
-        const { expression } = generateExpression(seed);
-        expect(tokenize(expression).errors).toEqual([]);
-      });
-    }
-  });
-}
-- 
GitLab