From b6c190f4e0a1759e9a9936885749c1733c207768 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 29 Jul 2023 09:51:12 -0400 Subject: [PATCH 1/4] insidious JS arrays are reference-based bug --- src/hooks/useCellar.test.ts | 4 +- src/plumbing/resolve.test.ts | 72 +++++++++++++++++++++++++++++++++++- src/utils/semver.test.ts | 7 ++++ src/utils/semver.ts | 2 +- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/hooks/useCellar.test.ts b/src/hooks/useCellar.test.ts index 86f21cd..753c881 100644 --- a/src/hooks/useCellar.test.ts +++ b/src/hooks/useCellar.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "deno/testing/asserts.ts" +import { assertEquals, assertRejects } from "deno/testing/asserts.ts" import SemVer, * as semver from "../utils/semver.ts" import { useTestConfig } from "./useTestConfig.ts" import install from "../plumbing/install.ts" @@ -14,6 +14,8 @@ Deno.test("useCellar.resolve()", async () => { await useCellar().resolve(installation.pkg) await useCellar().resolve({ project: "python.org", constraint: new semver.Range("^3") }) await useCellar().resolve(installation.path) + + assertRejects(() => useCellar().resolve({ project: "python.org", constraint: new semver.Range("@300")})) }) Deno.test("useCellar.has()", async () => { diff --git a/src/plumbing/resolve.test.ts b/src/plumbing/resolve.test.ts index a1701b1..4b894b7 100644 --- a/src/plumbing/resolve.test.ts +++ b/src/plumbing/resolve.test.ts @@ -1,8 +1,10 @@ +import { assert, assertEquals, fail, assertRejects } from "deno/testing/asserts.ts" import { Installation, Package, PackageRequirement } from "../types.ts" -import { assert, assertEquals, fail } from "deno/testing/asserts.ts" import { useTestConfig } from "../hooks/useTestConfig.ts" +import useInventory from "../hooks/useInventory.ts" import resolve, { _internals } from "./resolve.ts" import useCellar from "../hooks/useCellar.ts" +import * as semver from "../utils/semver.ts" import { stub } from "deno/testing/mock.ts" import SemVer from "../utils/semver.ts" import Path from "../utils/Path.ts" @@ -58,7 +60,7 @@ Deno.test("resolve cellar.has", async runner => { assert(errord) }) - await runner.step("uses existing version if even if update set", async () => { + await runner.step("uses existing version if even if update set", async () => { const stub1 = stub(_internals, "useInventory", () => ({ get: () => fail(), select: () => Promise.resolve(pkg.version), @@ -77,3 +79,69 @@ Deno.test("resolve cellar.has", async runner => { } }) }) + +const permissions = { net: false, read: true, env: ["TMPDIR", "HOME"], write: true /*FIXME*/ } + +// https://github.com/teaxyz/cli/issues/655 +Deno.test("postgres@500 fails", { permissions }, async () => { + useTestConfig() + + const pkg = { + project: "posqtgres.org", + version: new SemVer("15.0.1") + } + + const select = useInventory().select + const stub1 = stub(_internals, "useInventory", () => ({ + get: () => Promise.resolve([pkg.version]), + select, + })) + + const pkgs = [ + { project: pkg.project, constraint: new semver.Range('@500') } + ] + + try { + // https://github.com/teaxyz/cli/issues/655 + await assertRejects(() => resolve(pkgs)) + } finally { + stub1.restore() + } +}) + +// https://github.com/teaxyz/cli/issues/655 +Deno.test("postgres@500 fails if installed", { permissions }, async () => { + const pkg = { + project: "posqtgres.org", + version: new SemVer("15.0.1") + } + const prefix = useTestConfig().prefix + + const cellar = useCellar() + const has = (_: Path | Package | PackageRequirement) => { + const a: Installation = {pkg, path: prefix.join(pkg.project, `v${pkg.version}`) } + return Promise.resolve(a) + } + + const select = useInventory().select + const stub1 = stub(_internals, "useInventory", () => ({ + get: () => Promise.resolve([pkg.version]), + select, + })) + const stub2 = stub(_internals, "useCellar", () => ({ + ...cellar, + has + })) + + const pkgs = [ + { project: pkg.project, constraint: new semver.Range('@500') } + ] + + try { + // https://github.com/teaxyz/cli/issues/655 + await assertRejects(() => resolve(pkgs)) + } finally { + stub1.restore() + stub2.restore() + } +}) diff --git a/src/utils/semver.test.ts b/src/utils/semver.test.ts index 523d3e6..99b1cdb 100644 --- a/src/utils/semver.test.ts +++ b/src/utils/semver.test.ts @@ -45,6 +45,9 @@ Deno.test("semver", async test => { await test.step("satisfies", () => { assertEquals(new semver.Range("=3.1.0").max([new SemVer("3.1.0")]), new SemVer("3.1.0")) + + assertEquals(new semver.Range("^300").max([new SemVer("3.1.0")]), undefined) + assertEquals(new semver.Range("@300").max([new SemVer("3.1.0")]), undefined) }) await test.step("constructor", () => { @@ -153,6 +156,10 @@ Deno.test("semver", async test => { assertThrows(() => new semver.Range("1.2")) assertThrows(() => new semver.Range("1.2.3")) assertThrows(() => new semver.Range("1.2.3.4")) + + assertEquals(new semver.Range("@300").toString(), "^300") + assertEquals(new semver.Range("@300.1").toString(), "~300.1") + assertEquals(new semver.Range("@300.1.0").toString(), ">=300.1<300.1.1") }) await test.step("intersection", async test => { diff --git a/src/utils/semver.ts b/src/utils/semver.ts index 2ceaed8..b837c0f 100644 --- a/src/utils/semver.ts +++ b/src/utils/semver.ts @@ -180,7 +180,7 @@ export class Range { // @5.1 => latest 5.1.x (ie. ~5.1) // @5.1.0 => latest 5.1.0 (usually 5.1.0 since most stuff hasn't got more digits) const parts = match[2].split(".").map(x => parseInt(x)) - v1 = new SemVer(parts) + v1 = new SemVer([...parts]) const last = parts.pop()! v2 = new SemVer([...parts, last + 1]) return [v1, v2] From 4a81adc49a2a1e6b47bb6dd6f1b4e131c9354fee Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 29 Jul 2023 09:57:37 -0400 Subject: [PATCH 2/4] wip --- src/plumbing/resolve.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plumbing/resolve.test.ts b/src/plumbing/resolve.test.ts index 4b894b7..a699dce 100644 --- a/src/plumbing/resolve.test.ts +++ b/src/plumbing/resolve.test.ts @@ -80,7 +80,7 @@ Deno.test("resolve cellar.has", async runner => { }) }) -const permissions = { net: false, read: true, env: ["TMPDIR", "HOME"], write: true /*FIXME*/ } +const permissions = { net: false, read: true, env: ["TMPDIR", "HOME", "TMP"], write: true /*FIXME*/ } // https://github.com/teaxyz/cli/issues/655 Deno.test("postgres@500 fails", { permissions }, async () => { From f67f19a5c3a5532f0494bf5043aa23965b3a1a85 Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 29 Jul 2023 10:00:40 -0400 Subject: [PATCH 3/4] wip --- src/plumbing/resolve.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plumbing/resolve.test.ts b/src/plumbing/resolve.test.ts index a699dce..d2c66ca 100644 --- a/src/plumbing/resolve.test.ts +++ b/src/plumbing/resolve.test.ts @@ -80,7 +80,7 @@ Deno.test("resolve cellar.has", async runner => { }) }) -const permissions = { net: false, read: true, env: ["TMPDIR", "HOME", "TMP"], write: true /*FIXME*/ } +const permissions = { net: false, read: true, env: ["TMPDIR", "HOME", "TMP", "TEMP"], write: true /*FIXME*/ } // https://github.com/teaxyz/cli/issues/655 Deno.test("postgres@500 fails", { permissions }, async () => { From 7f8c88805bb4811ede3049368adc23816393934e Mon Sep 17 00:00:00 2001 From: Max Howell Date: Sat, 29 Jul 2023 10:39:38 -0400 Subject: [PATCH 4/4] wip --- src/plumbing/resolve.test.ts | 10 +++++++--- src/utils/semver.test.ts | 1 + src/utils/semver.ts | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plumbing/resolve.test.ts b/src/plumbing/resolve.test.ts index d2c66ca..126f46b 100644 --- a/src/plumbing/resolve.test.ts +++ b/src/plumbing/resolve.test.ts @@ -118,9 +118,13 @@ Deno.test("postgres@500 fails if installed", { permissions }, async () => { const prefix = useTestConfig().prefix const cellar = useCellar() - const has = (_: Path | Package | PackageRequirement) => { - const a: Installation = {pkg, path: prefix.join(pkg.project, `v${pkg.version}`) } - return Promise.resolve(a) + const has = (b: Path | Package | PackageRequirement) => { + if ("constraint" in b && b.constraint.satisfies(pkg.version)) { + const a: Installation = {pkg, path: prefix.join(pkg.project, `v${pkg.version}`) } + return Promise.resolve(a) + } else { + return Promise.resolve(undefined) + } } const select = useInventory().select diff --git a/src/utils/semver.test.ts b/src/utils/semver.test.ts index 99b1cdb..17f7384 100644 --- a/src/utils/semver.test.ts +++ b/src/utils/semver.test.ts @@ -46,6 +46,7 @@ Deno.test("semver", async test => { await test.step("satisfies", () => { assertEquals(new semver.Range("=3.1.0").max([new SemVer("3.1.0")]), new SemVer("3.1.0")) + // the following two test for https://github.com/teaxyz/lib/pull/36 assertEquals(new semver.Range("^300").max([new SemVer("3.1.0")]), undefined) assertEquals(new semver.Range("@300").max([new SemVer("3.1.0")]), undefined) }) diff --git a/src/utils/semver.ts b/src/utils/semver.ts index b837c0f..69e81a0 100644 --- a/src/utils/semver.ts +++ b/src/utils/semver.ts @@ -53,7 +53,7 @@ export default class SemVer { this.raw = v.raw this.pretty = v.pretty } else { - this.components = input + this.components = [...input] this.raw = input.join('.') } @@ -180,7 +180,7 @@ export class Range { // @5.1 => latest 5.1.x (ie. ~5.1) // @5.1.0 => latest 5.1.0 (usually 5.1.0 since most stuff hasn't got more digits) const parts = match[2].split(".").map(x => parseInt(x)) - v1 = new SemVer([...parts]) + v1 = new SemVer(parts) const last = parts.pop()! v2 = new SemVer([...parts, last + 1]) return [v1, v2]