This repository has been archived on 2026-11-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
qqq-orb/tests/test_node_npm_publish.py

172 lines
9.1 KiB
Python

#!/usr/bin/env python3
"""Exercise publisher boundaries without network access or real credentials."""
import json
import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / 'src/scripts/node_npm_publish.sh'
NODE = shutil.which('node')
NPM = shutil.which('npm')
class PublishTests(unittest.TestCase):
def run_publish(self, version='1.0.0-RC.1', trusted=True, project_config=False, trusted_mode=None, **overrides):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
bin_dir = root / 'bin'
bin_dir.mkdir()
(root / 'package.json').write_text(json.dumps({'version': version}) + '\n')
if project_config:
(root / '.npmrc').write_text('//registry.npmjs.org/:_authToken=synthetic-old-token\n')
scripts = {
'npm': '''#!/usr/bin/env python3
import json, os, pathlib, subprocess, sys
if sys.argv[1:] == ['--version']:
print(os.environ.get('TEST_NPM_VERSION', '11.19.0'))
else:
assert sys.argv[1] == 'publish', 'Unexpected npm command'
if os.environ['QQQ_NPM_TRUSTED_PUBLISHING'] in ['true', '1']:
assert os.environ.get('NPM_ID_TOKEN') == 'synthetic-oidc-token'
assert all(not os.environ.get(name) for name in ['NPM_TOKEN', 'NODE_AUTH_TOKEN', 'NPM_AUTH_TOKEN'])
assert os.environ['NPM_CONFIG_USERCONFIG'] != os.environ['NPM_CONFIG_GLOBALCONFIG']
assert not pathlib.Path(os.environ['NPM_CONFIG_USERCONFIG']).exists()
assert not pathlib.Path(os.environ['NPM_CONFIG_GLOBALCONFIG']).exists()
registry = subprocess.check_output([os.environ['TEST_REAL_NPM'], 'config', 'get', 'registry'], text=True)
assert registry.strip() == 'https://registry.npmjs.org/'
pathlib.Path('published.json').write_text(json.dumps(sys.argv[1:]))
sys.exit(int(os.environ.get('TEST_PUBLISH_STATUS', '0')))
''',
'circleci': '''#!/usr/bin/env python3
import os, pathlib, sys
assert sys.argv[1:] == ['run', 'oidc', 'get', '--claims', '{"aud":"npm:registry.npmjs.org"}']
pathlib.Path('oidc-called').touch()
print(os.environ.get('TEST_OIDC_VALUE', 'synthetic-oidc-token'))
sys.exit(int(os.environ.get('TEST_OIDC_STATUS', '0')))
''',
'node': '''#!/usr/bin/env python3
import os, subprocess, sys
if sys.argv[1] != '-':
os.execv(os.environ['TEST_REAL_NODE'], [os.environ['TEST_REAL_NODE'], *sys.argv[1:]])
source = sys.stdin.read()
if 'TEST_NODE_VERSION' in os.environ:
source = source.replace('process.versions.node', repr(os.environ['TEST_NODE_VERSION']))
sys.exit(subprocess.run([os.environ['TEST_REAL_NODE'], *sys.argv[1:]], input=source, text=True).returncode)
'''
}
for name, contents in scripts.items():
executable = bin_dir / name
executable.write_text(contents)
executable.chmod(0o755)
env = dict(os.environ, PATH=str(bin_dir) + os.pathsep + os.environ['PATH'],
QQQ_NPM_TRUSTED_PUBLISHING=str(trusted).lower() if trusted_mode is None else trusted_mode,
TEST_REAL_NODE=NODE, TEST_REAL_NPM=NPM,
NPM_TOKEN='synthetic-old-token', NODE_AUTH_TOKEN='synthetic-old-token',
NPM_AUTH_TOKEN='synthetic-old-token', **overrides)
result = subprocess.run(['bash', '-x', str(SCRIPT)], cwd=root, env=env,
text=True, capture_output=True)
self.assertNotIn('synthetic-oidc-token', result.stdout + result.stderr)
self.assertNotIn('synthetic-old-token', result.stdout + result.stderr)
published = root / 'published.json'
return result, json.loads(published.read_text()) if published.exists() else None, (root / 'oidc-called').exists()
def test_tags_and_legacy_authentication(self):
for trusted in [True, False]:
for version, tag in [('1.0.0-RC.1', 'rc'), ('1.0.0-SNAPSHOT', 'snapshot'),
('1.0.0-SNAPSHOT.101', 'snapshot'),
('1.0.0-alpha.1', 'next'), ('1.0.0-beta.1', 'next'), ('1.0.0', 'latest')]:
with self.subTest(trusted=trusted, version=version):
result, args, oidc = self.run_publish(version, trusted)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(args, ['publish', '--access', 'public', '--tag', tag])
self.assertEqual(oidc, trusted)
def test_circleci_boolean_environment_values(self):
for mode, expected_oidc in [('1', True), ('0', False)]:
with self.subTest(mode=mode):
result, args, oidc = self.run_publish(trusted_mode=mode)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(args, ['publish', '--access', 'public', '--tag', 'rc'])
self.assertEqual(oidc, expected_oidc)
def test_oidc_failure_or_empty_token_stops_publication(self):
for overrides in [{'TEST_OIDC_STATUS': '7'}, {'TEST_OIDC_VALUE': ''}]:
with self.subTest(overrides=overrides):
result, args, oidc = self.run_publish(**overrides)
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(args)
self.assertTrue(oidc)
def test_unsupported_toolchains_stop_before_requesting_identity(self):
for overrides in [{'TEST_NPM_VERSION': '11.5.0'}, {'TEST_NPM_VERSION': '10.9.0'},
{'TEST_NODE_VERSION': '22.13.1'}, {'TEST_NODE_VERSION': '20.20.0'}]:
with self.subTest(overrides=overrides):
result, args, oidc = self.run_publish(**overrides)
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(args)
self.assertFalse(oidc)
def test_minimum_toolchain_is_supported(self):
result, args, oidc = self.run_publish(TEST_NPM_VERSION='11.5.1', TEST_NODE_VERSION='22.14.0')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIsNotNone(args)
self.assertTrue(oidc)
def test_project_or_environment_credentials_are_rejected(self):
for overrides in [{'project_config': True}, {'NPM_CONFIG__AUTH': 'synthetic-old-token'},
{'npm_config_userconfig': '/synthetic/legacy.npmrc'},
{'npm_config_USERCONFIG': '/synthetic/legacy.npmrc'},
{'Npm_Config_Globalconfig': '/synthetic/legacy.npmrc'}]:
with self.subTest(overrides=overrides):
result, args, oidc = self.run_publish(**overrides)
self.assertNotEqual(result.returncode, 0)
self.assertIsNone(args)
self.assertFalse(oidc)
def test_publish_failure_is_not_reported_as_success(self):
result, args, oidc = self.run_publish(TEST_PUBLISH_STATUS='9')
self.assertEqual(result.returncode, 9)
self.assertIsNotNone(args)
self.assertTrue(oidc)
self.assertNotIn('Successfully published', result.stdout)
class SnapshotVersionTests(unittest.TestCase):
def test_snapshot_jobs_keep_source_head_and_publish_distinct_versions(self):
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
def git(*args):
return subprocess.check_output(['git', *args], cwd=root, text=True).strip()
git('init', '-q', '-b', 'develop')
source = '{"name": "snapshot-test", "version": "0.40.27-SNAPSHOT"}\n'
package = root / 'package.json'
package.write_text(source)
git('add', 'package.json')
git('-c', 'user.name=Test', '-c', 'user.email=test@example.invalid',
'-c', 'commit.gpgsign=false', 'commit', '-qm', 'initial source')
head = git('rev-parse', 'HEAD')
for number in ('101', '102'):
package.write_text(source)
env = dict(os.environ, CIRCLE_BUILD_NUM=number, BRANCH_TYPE='snapshot')
subprocess.run(['bash', str(SCRIPT.parent / 'sync-npm-version.sh')],
cwd=root, env=env, check=True, capture_output=True)
self.assertEqual(json.loads(package.read_text())['version'], '0.40.28-SNAPSHOT.' + number)
subprocess.run(['bash', str(SCRIPT.parent / 'node_version_commit.sh')],
cwd=root, env=env, check=True, capture_output=True)
self.assertEqual(git('rev-parse', 'HEAD'), head)
self.assertEqual(git('diff', '--cached', '--name-only'), '')
for number in ('', '0', '01', '-1', 'abc'):
package.write_text(source)
result = subprocess.run(['bash', str(SCRIPT.parent / 'sync-npm-version.sh')],
cwd=root, env=dict(os.environ, CIRCLE_BUILD_NUM=number),
capture_output=True)
self.assertNotEqual(result.returncode, 0)
self.assertEqual(package.read_text(), source)
if __name__ == '__main__':
unittest.main()