Skip to content

Commit 2760d68

Browse files
committed
add more unit tests for catching invalid lockfiles early
1 parent fe14519 commit 2760d68

File tree

5 files changed

+100
-62
lines changed

5 files changed

+100
-62
lines changed

lib/internal/nodejsLockUtils.nix

+16-36
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,21 @@
99

1010
sanitizeLockfile = lock:
1111
# Every project MUST have a name
12-
assert lock ? name;
13-
# Every project MUST have a version
14-
assert lock ? version;
15-
# This lockfile module only supports lockfileVersion 2 and 3
16-
assert !lock ? lockfileVersion || lock.lockfileVersion >= 2;
17-
# The Lockfile must contain a 'packages' attribute.
18-
assert lock ? packages;
19-
lock;
12+
if ! lock ? name
13+
then throw "Invalid lockfile: Every project MUST have a name"
14+
else
15+
# Every project MUST have a version
16+
if ! lock ? version
17+
then throw "Invalid lockfile: Every project MUST have a version"
18+
else
19+
# This lockfile module only supports lockfileVersion 2 and 3
20+
if ! lock ? lockfileVersion || lock.lockfileVersion <= 1
21+
then throw "This lockfile module only supports lockfileVersion 2 and 3"
22+
else
23+
# The Lockfile must contain a 'packages' attribute.
24+
if ! lock ? packages
25+
then throw "Invalid lockfile: The Lockfile must contain 'packages' attribute."
26+
else lock;
2027

2128
findEntry =
2229
# = "attrs"
@@ -34,33 +41,6 @@
3441
else if currentPath == ""
3542
then throw "${search} not found in package-lock.json."
3643
else findEntry packageLock (stripPath currentPath) search;
37-
38-
# Returns the names of all "bundledDependencies".
39-
# People depend on different types and different names. Unfortunatly those fields are not part of the offical npm documentation.
40-
# Which may also be the reason for the mess.
41-
#
42-
# TODO: define unit tests.
43-
# Adopted from https://github.com/aakropotkin/floco/blob/708c4ffa0c05033c29fe6886a238cb20c3ba3fb4/modules/plock/implementation.nix#L139
44-
#
45-
# getBundledDependencies :: Pent -> {}
46-
getBundledDependencies = pent: let
47-
# b :: bool | []
48-
b = pent.bundledDependencies or pent.bundleDependencies or [];
49-
in
50-
# The following asserts is the XOR logic.
51-
# "bundle" and "bundled" dependencies are both valid but invalid if both or none keys exist
52-
assert ( pent ? bundledDependencies ) ->
53-
( ! ( pent ? bundleDependencies ) );
54-
assert ( pent ? bundleDependencies ) ->
55-
( ! ( pent ? bundledDependencies ) );
56-
if b == [] then {} else
57-
if builtins.isList b then { bundledDependencies = b; } else
58-
if ! b then {} else {
59-
# b :: true
60-
bundledDependencies = builtins.attrNames (
61-
( pent.dependencies or {} ) // ( pent.requires or {} )
62-
);
63-
};
6444
in {
65-
inherit findEntry stripPath getBundledDependencies sanitizeLockfile;
45+
inherit findEntry stripPath sanitizeLockfile;
6646
}

modules/dream2nix/nodejs-package-lock-v3/default.nix

+10-12
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
inherit (config.deps) fetchurl;
1111

12-
nodejsLockUtils = import ../../../lib/internal/nodejsLockUtils.nix { inherit lib; };
12+
nodejsLockUtils = import ../../../lib/internal/nodejsLockUtils.nix {inherit lib;};
1313

1414
# Collection of sanitized functions that always return the same type
1515
isLink = pent: pent.link or false;
@@ -21,10 +21,11 @@
2121
# getBin = pent: pent.bin or {};
2222

2323
/*
24-
Pent :: {
25-
See: https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#packages
26-
}
27-
pent is one entry of 'packages'
24+
Pent :: {
25+
See: https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json#packages
26+
}
27+
> We should mention that docs are imcomplete on npmjs.com
28+
pent is one entry of 'packages'
2829
*/
2930
parseSource = pent:
3031
if isLink pent
@@ -37,20 +38,16 @@
3738
hash = pent.integrity;
3839
};
3940

40-
4141
getDependencies = lock: path: pent:
42-
l.mapAttrs (depName: _semverConstraint:
43-
let
42+
l.mapAttrs (depName: _semverConstraint: let
4443
packageIdent = nodejsLockUtils.findEntry lock path depName;
4544
depPent = lock.packages.${packageIdent};
46-
in
47-
{
45+
in {
4846
dev = pent.dev or false;
4947
version = depPent.version;
5048
})
5149
(pent.dependencies or {} // pent.devDependencies or {} // pent.optionalDependencies or {});
5250

53-
5451
# Takes one entry of "package" from package-lock.json
5552
parseEntry = lock: path: entry:
5653
if path == ""
@@ -84,7 +81,8 @@
8481
};
8582
};
8683

87-
mergePdefs = builtins.foldl'
84+
mergePdefs =
85+
builtins.foldl'
8886
(acc: entry:
8987
acc
9088
// {

modules/dream2nix/nodejs-package-lock-v3/interface.nix

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ in {
7171
/*
7272
7373
type: pdefs.${name}.${version} :: {
74-
74+
7575
// Pointing to the source of the package.
7676
// in most cases this is a tarball (tar.gz) which needs to be unpacked by e.g. unpackPhase
7777
source :: Derivation | Path
@@ -86,7 +86,7 @@ in {
8686
}
8787
}
8888
}
89-
*/
89+
*/
9090
pdefs = {
9191
type = t.attrsOf (t.attrsOf (t.submodule {
9292
options.dependencies = l.mkOption {

tests/nix-unit/test_nodejs_lock_v3/default.nix

+7-9
Original file line numberDiff line numberDiff line change
@@ -297,18 +297,16 @@ in {
297297
imports = [
298298
dream2nix.modules.dream2nix.nodejs-package-lock-v3
299299
];
300-
nodejs-package-lock-v3.packageLock = lib.mkForce {
301-
# Example content of lockfile
302-
# "lockfileVersion" = 1;
303-
};
300+
nodejs-package-lock-v3.packageLock =
301+
lib.mkForce {
302+
};
304303
};
305304
config = evaled.config;
306305
in {
307-
expr = builtins.tryEval (config.nodejs-package-lock-v3.pdefs);
308-
expected = {
309-
success = false;
310-
value = false;
306+
expr = config.nodejs-package-lock-v3.pdefs;
307+
expectedError = {
308+
type = "ThrownError";
309+
msg = "Invalid lockfile";
311310
};
312311
};
313-
314312
}

tests/nix-unit/test_nodejs_lockutils/default.nix

+65-3
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474
expr = path;
7575
expected = "node_modules/underscore";
7676
};
77-
78-
# test the lock
77+
78+
# test the lock
7979
test_nodejsLockUtils_lockfile_v3 = let
8080
plock = {
8181
name = "foo";
@@ -110,7 +110,69 @@
110110
};
111111
in {
112112
expr = nodejsLockUtils.sanitizeLockfile plock;
113-
expectedError = plock;
113+
expectedError = {
114+
type = "ThrownError";
115+
msg = "This lockfile module only supports lockfileVersion 2 and 3";
116+
};
114117
};
115118

119+
test_nodejsLockUtils_lockfile_missing_name = let
120+
plock = {
121+
# name = "foo";
122+
version = "1.0.0";
123+
lockfileVersion = 3;
124+
packages = {};
125+
};
126+
in {
127+
expr = nodejsLockUtils.sanitizeLockfile plock;
128+
expectedError = {
129+
type = "ThrownError";
130+
msg = "MUST have a name";
131+
};
132+
};
133+
134+
test_nodejsLockUtils_lockfile_missing_version = let
135+
plock = {
136+
name = "foo";
137+
# version = "1.0.0";
138+
lockfileVersion = 3;
139+
packages = {};
140+
};
141+
in {
142+
expr = nodejsLockUtils.sanitizeLockfile plock;
143+
expectedError = {
144+
type = "ThrownError";
145+
msg = "MUST have a version";
146+
};
147+
};
148+
149+
test_nodejsLockUtils_lockfile_missing_lockfileVersion = let
150+
plock = {
151+
name = "foo";
152+
version = "1.0.0";
153+
# lockfileVersion = 3;
154+
packages = {};
155+
};
156+
in {
157+
expr = nodejsLockUtils.sanitizeLockfile plock;
158+
expectedError = {
159+
type = "ThrownError";
160+
msg = "lockfileVersion";
161+
};
162+
};
163+
164+
test_nodejsLockUtils_lockfile_missing_packages = let
165+
plock = {
166+
name = "foo";
167+
version = "1.0.0";
168+
lockfileVersion = 3;
169+
# packages = {};
170+
};
171+
in {
172+
expr = nodejsLockUtils.sanitizeLockfile plock;
173+
expectedError = {
174+
type = "ThrownError";
175+
msg = "must contain 'packages'";
176+
};
177+
};
116178
}

0 commit comments

Comments
 (0)