diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml deleted file mode 100644 index 6e573059c1..0000000000 --- a/.github/workflows/unit-test.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: "Unit test" - -on: - pull_request: - branches: [main] - -jobs: - unit-test: - name: Unit test - runs-on: ubuntu-latest - steps: - - name: Install Nix - uses: DeterminateSystems/nix-installer-action@v4 - - name: Run the Magic Nix Cache - uses: DeterminateSystems/magic-nix-cache-action@v2 - - uses: actions/checkout@v3 - - run: cd core/api && nix develop -c pnpm install --frozen-lockfile - - name: Run unit tests - run: cd core/api && nix develop -c make unit-in-ci diff --git a/core/api/BUCK b/core/api/BUCK index a3179e0949..1b6c7462e8 100644 --- a/core/api/BUCK +++ b/core/api/BUCK @@ -10,6 +10,7 @@ load( "typescript_check", "yaml_check", "madge_check", + "test_unit", ) load("@toolchains//rover:macros.bzl", "sdl", "diff_check", "dev_update_file") @@ -155,6 +156,16 @@ madge_check( srcs = [":src"], ) +test_unit( + name = "unit-tests", + srcs = [":src"] + [":test_src"] + glob([".env", "galoy.yaml"]), + config_file = "test/unit/jest.config.js", + env_file = ".env", + env = { + "LOGLEVEL": "warn", + } +) + test_suite( name = "test-unit", tests = [ @@ -165,5 +176,6 @@ test_suite( ":check-circular-dependencies", ":public-schema-diff", ":admin-schema-diff", + ":unit-tests", ], ) diff --git a/toolchains/workspace-pnpm/macros.bzl b/toolchains/workspace-pnpm/macros.bzl index bf40792efd..878748bb49 100644 --- a/toolchains/workspace-pnpm/macros.bzl +++ b/toolchains/workspace-pnpm/macros.bzl @@ -592,10 +592,14 @@ def _npm_test_impl( cmd_args([build_context.workspace_root, ctx.label.package], delimiter = "/"), "--bin", cmd_args(program_run_info), - "--", - program_args, ]) + if hasattr(ctx.attrs, 'env_file'): + run_cmd_args.add("--env-file") + run_cmd_args.add(ctx.attrs.env_file) + + run_cmd_args.add("--") + run_cmd_args.add(program_args) args_file = ctx.actions.write("args.txt", run_cmd_args) return inject_test_run_info( @@ -603,6 +607,8 @@ def _npm_test_impl( ExternalRunnerTestInfo( type = test_info_type, command = [run_cmd_args], + env = ctx.attrs.env, + labels = ctx.attrs.labels, ), ) + [ DefaultInfo(default_output = args_file), @@ -723,6 +729,18 @@ _eslint = rule( "node_modules": attrs.source( doc = """Target which builds `node_modules`.""", ), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Set environment variables for this rule's invocation of eslint. The environment + variable values may include macros which are expanded.""", + ), + "labels": attrs.list( + attrs.string(), + default = [], + ), "_inject_test_env": attrs.default_only( attrs.dep(default = "prelude//test/tools:inject_test_env"), ), @@ -791,6 +809,18 @@ _typescript_check = rule( "node_modules": attrs.source( doc = """Target which builds package `node_modules`.""", ), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Set environment variables for this rule's invocation of tsc. The environment + variable values may include macros which are expanded.""", + ), + "labels": attrs.list( + attrs.string(), + default = [], + ), "_inject_test_env": attrs.default_only( attrs.dep(default = "prelude//test/tools:inject_test_env"), ), @@ -854,6 +884,18 @@ _yaml_check = rule( "node_modules": attrs.source( doc = """Target which builds package `node_modules`.""", ), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Set environment variables for this rule's invocation of prettier. The environment + variable values may include macros which are expanded.""", + ), + "labels": attrs.list( + attrs.string(), + default = [], + ), "_inject_test_env": attrs.default_only( attrs.dep(default = "prelude//test/tools:inject_test_env"), ), @@ -919,6 +961,18 @@ _madge_check = rule( "node_modules": attrs.source( doc = """Target which builds package `node_modules`.""", ), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Set environment variables for this rule's invocation of madge. The environment + variable values may include macros which are expanded.""", + ), + "labels": attrs.list( + attrs.string(), + default = [], + ), "_inject_test_env": attrs.default_only( attrs.dep(default = "prelude//test/tools:inject_test_env"), ), @@ -1006,3 +1060,88 @@ dev_pnpm_task_test = rule(impl = pnpm_task_test_impl, attrs = { "srcs": attrs.list(attrs.source(), default = [], doc = """List of sources we require"""), "deps": attrs.list(attrs.source(), default = [], doc = """List of dependencies we require"""), }) + +def test_unit_impl(ctx: AnalysisContext) -> list[[ + DefaultInfo, + RunInfo, + ExternalRunnerTestInfo, +]]: + args = cmd_args() + args.add("--config") + args.add(ctx.attrs.config_file) + args.add("--bail") + args.add("--verbose") + + return _npm_test_impl( + ctx, + ctx.attrs.jest[RunInfo], + args, + "jest", + ) + +_test_unit = rule( + impl = test_unit_impl, + attrs = { + "srcs": attrs.list( + attrs.source(), + default = [], + doc = """List of package source files to track.""", + ), + "jest": attrs.dep( + providers = [RunInfo], + doc = """jest dependency.""", + ), + "config_file": attrs.option( + attrs.string(), + doc = """File name and relative path for jest config.""", + ), + "env_file": attrs.option( + attrs.string(), + doc = """File name and relative path for env variables required.""", + ), + "env": attrs.dict( + key = attrs.string(), + value = attrs.arg(), + sorted = False, + default = {}, + doc = """Set environment variables for this rule's invocation of jest. The environment + variable values may include macros which are expanded.""", + ), + "labels": attrs.list( + attrs.string(), + default = [], + ), + "node_modules": attrs.source( + doc = """Target which builds package `node_modules`.""", + ), + "_inject_test_env": attrs.default_only( + attrs.dep(default = "prelude//test/tools:inject_test_env"), + ), + "_python_toolchain": attrs.toolchain_dep( + default = "toolchains//:python", + providers = [PythonToolchainInfo], + ), + "_workspace_pnpm_toolchain": attrs.toolchain_dep( + default = "toolchains//:workspace_pnpm", + providers = [WorkspacePnpmToolchainInfo], + ), + }, +) + +def test_unit( + node_modules = ":node_modules", + visibility = ["PUBLIC"], + **kwargs): + jest_bin = "jest_bin" + if not rule_exists(jest_bin): + npm_bin( + name = jest_bin, + bin_name = "jest", + ) + + _test_unit( + jest = ":{}".format(jest_bin), + node_modules = node_modules, + visibility = visibility, + **kwargs, + ) diff --git a/toolchains/workspace-pnpm/run_in_dir.py b/toolchains/workspace-pnpm/run_in_dir.py index da6ce99d3d..b2fa463626 100644 --- a/toolchains/workspace-pnpm/run_in_dir.py +++ b/toolchains/workspace-pnpm/run_in_dir.py @@ -7,12 +7,24 @@ import subprocess import sys -def compute_path(arg: str) -> str: - if arg.endswith("::abspath"): - return os.path.abspath(arg.removesuffix("::abspath")) +def merge_env_from_file(file_path): + # Shell command to source the .env file + if file_path and os.path.exists(file_path): + cmd = f'source {file_path} && env' else: - return arg + cmd = f'env' + result = subprocess.run(cmd, capture_output=True, text=True, shell=True, executable="/bin/bash") + if result.returncode != 0: + raise RuntimeError(result.stderr) + + lines = result.stdout.strip().split('\n') + env_dict = {} + for line in lines: + key, value = line.split('=', 1) + env_dict[key] = value + + return env_dict if __name__ == "__main__": parser = argparse.ArgumentParser(description=__doc__) @@ -24,6 +36,10 @@ def compute_path(arg: str) -> str: "--bin", help="Binary to execute program with", ) + parser.add_argument( + "--env-file", + help="Env file to load variables from", + ) parser.add_argument( "args", help="Program and arguments", @@ -34,6 +50,7 @@ def compute_path(arg: str) -> str: bin_args = args.args[1:] # ignore '--' separator cmd = [os.path.abspath(args.bin), *bin_args] - exit_code = subprocess.call(cmd, cwd=args.cwd) + env = merge_env_from_file(args.env_file) + exit_code = subprocess.call(cmd, cwd=args.cwd, env=env) sys.exit(exit_code)