Skip to content

Commit 339ad30

Browse files
committed
Addressed coverage and testing
1 parent 137d62c commit 339ad30

9 files changed

Lines changed: 418 additions & 18 deletions

File tree

gems/test_import2.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
# test.py
66

77
import json
8-
import js2pysecrets
8+
from js2pysecrets import share, combine
9+
import js2pysecrets.node as node
10+
911

1012
# from js2pysecrets import wrapper
1113
#
@@ -24,13 +26,15 @@
2426
# print(js2pysecrets.str2hex(data))
2527
#random = js2pysecrets.jsFunction('random', test=True)
2628
#print(random(32))
27-
print(js2pysecrets.random(32))
28-
print(js2pysecrets.share("ababab", 6, 3, test=True))
29+
print(node.random(32))
30+
print(share("ababab", 6, 3, test=True))
2931

30-
shares = js2pysecrets.share("ababab", 6, 3)
32+
shares = node.share("ababab", 6, 3)
3133
print(shares[1])
3234
print(shares[3])
3335
print(shares[5])
3436

35-
recoveredPass = js2pysecrets.combine([shares[1],shares[3],shares[5]])
36-
print("recovered password is: ", recoveredPass)
37+
recoveredPass = combine([shares[1],shares[3],shares[5]])
38+
print("recovered password is: ", recoveredPass)
39+
40+
node.random(0)

javascript/wrapper.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// wrapper.js
2+
3+
/*
4+
A revised and simplified version of JavaScript the wrapper. Accepts multiple JavaScript commands for the secrets.js package, returning the output of the last command.
5+
*/
6+
7+
// Hex to ASCII
8+
function hex2a(hex)
9+
{
10+
var str = '';
11+
for (var i = 0; i < hex.length; i += 2)
12+
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
13+
return str;
14+
}
15+
16+
// Require the secrets package and assign it to a variable
17+
const secrets = require('../node_modules/secrets.js-grempe/secrets.js');
18+
19+
// If the script is executed directly, call the appropriate function based on arguments
20+
if (require.main === module) {
21+
const commands = hex2a(process.argv[2]);
22+
23+
if (!commands.length) {
24+
console.error("No commands provided.");
25+
process.exit(1);
26+
}
27+
28+
try {
29+
// The the commands to execute
30+
const inputData = JSON.parse(commands);
31+
32+
// Debugging statements
33+
//console.log("Commands received from Python:", commands);
34+
//console.log("inputData[0][0]: ", inputData[0][0]); // returns: init(33)
35+
36+
// Loop through commands
37+
for (const command of inputData) {
38+
// Evaluate each command
39+
lastResult = eval('secrets.' + command);
40+
}
41+
42+
// Return the result of the last command
43+
console.log(JSON.stringify(lastResult));
44+
45+
} catch (error) {
46+
console.error("Error executing command:", error.message);
47+
process.exit(1);
48+
}
49+
}

js2pysecrets/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .base import * # noqa
2+
3+
# import .node # noqa

js2pysecrets/base.py

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,61 @@
1-
"""
2-
turbo_parakeet base module.
1+
# import json
32

4-
This is the principal module of the turbo_parakeet project.
5-
here you put your main classes and objects.
3+
from .decorators import JsFunction, jsNeedless
64

7-
Be creative! do whatever you want!
5+
# from .wrapper import wrapper # Import your wrapper function
86

9-
If you want to replace this with a Flask application run:
7+
NAME = "js2pysecrets"
108

11-
$ make init
129

13-
and then choose `flask` as template.
14-
"""
10+
@JsFunction
11+
def share(*args, **kwargs):
12+
pass # pragma: no cover
1513

16-
# example constant variable
17-
NAME = "js2pysecrets"
14+
15+
@JsFunction
16+
def random(*args, **kwargs):
17+
pass # pragma: no cover
18+
19+
20+
@JsFunction
21+
def combine(*args, **kwargs):
22+
pass # pragma: no cover
23+
24+
25+
@jsNeedless
26+
def init(*args, **kwargs):
27+
pass # pragma: no cover
28+
29+
30+
# # Core Functions from secrets.js
31+
# init = jsFunction('init')
32+
# combine = jsFunction('combine')
33+
# getConfig = jsFunction('getConfig')
34+
# extractShareComponents = jsFunction('extractShareComponents')
35+
# setRNG = jsFunction('setRNG')
36+
# str2hex = jsFunction('str2hex')
37+
# hex2str = jsFunction('hex2str')
38+
# random = jsFunction('random')
39+
# share = jsFunction('share')
40+
# newShare = jsFunction('newShare')
41+
#
42+
# # Test Functions
43+
# _reset = jsNeedless('_reset')
44+
# _isSetRNG = jsFunction('_isSetRNG')
45+
46+
# /* test-code */
47+
# // export private functions so they can be unit tested directly.
48+
# _reset: reset,
49+
# _padLeft: padLeft,
50+
# _hex2bin: hex2bin,
51+
# _bin2hex: bin2hex,
52+
# _hasCryptoGetRandomValues: hasCryptoGetRandomValues,
53+
# _hasCryptoRandomBytes: hasCryptoRandomBytes,
54+
# _getRNG: getRNG,
55+
# _isSetRNG: isSetRNG,
56+
# _splitNumStringToIntArray: splitNumStringToIntArray,
57+
# _horner: horner,
58+
# _lagrange: lagrange,
59+
# _getShares: getShares,
60+
# _constructPublicShareString: constructPublicShareString
61+
# /* end-test-code */

js2pysecrets/decorators.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import json
2+
import types
3+
4+
from .wrapper import wrapper # Import your wrapper function
5+
6+
7+
class JsFunction:
8+
def __init__(self, func, test=False):
9+
self.func = func
10+
self.test = test
11+
12+
def __call__(self, *args, test=False, **kwargs):
13+
def wrapped_func(*args, **kwargs):
14+
args_str = ", ".join(repr(arg) for arg in args)
15+
16+
return f"{self.func.__name__}({args_str})"
17+
18+
data = []
19+
20+
# DO NOT REMOVE THIS
21+
if test or self.test:
22+
data.append("setRNG('testRandom')")
23+
24+
data.append(
25+
wrapped_func(*args, **kwargs)
26+
if args
27+
else self.func(*args, **kwargs)
28+
)
29+
30+
json_data = json.dumps(data, indent=None).replace("'", "`")
31+
commands = json_data.encode().hex()
32+
results = wrapper(commands)
33+
34+
return results
35+
36+
def __get__(self, instance, owner):
37+
return (
38+
self if instance is None else types.MethodType(self, instance)
39+
) # pragma: no cover No idea how to test!
40+
41+
42+
class jsNeedless:
43+
def __init__(self, func_name, test=False):
44+
self.func_name = func_name
45+
self.test = test
46+
47+
def __call__(self, *args, **kwargs):
48+
raise Exception(
49+
"Calling subsequent JavaScript functions are not supported. -or- "
50+
"The JavaScript function isn't necessary for the Python version."
51+
)

js2pysecrets/node.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# import json
2+
3+
from .decorators import JsFunction, jsNeedless
4+
5+
# from .wrapper import wrapper # Import your wrapper function
6+
7+
NAME = "js2pysecrets"
8+
9+
10+
@JsFunction
11+
def share(*args, **kwargs):
12+
pass # pragma: no cover
13+
14+
15+
@JsFunction
16+
def random(*args, **kwargs):
17+
pass # pragma: no cover
18+
19+
20+
@JsFunction
21+
def getConfig(*args, **kwargs):
22+
pass # pragma: no cover
23+
24+
25+
@JsFunction
26+
def combine(*args, **kwargs):
27+
pass # pragma: no cover
28+
29+
30+
@jsNeedless
31+
def init(*args, **kwargs):
32+
pass # pragma: no cover
33+
34+
35+
@jsNeedless
36+
def newShare(*args, **kwargs):
37+
pass # pragma: no cover
38+
39+
40+
# # Core Functions from secrets.js
41+
# init = jsFunction('init')
42+
# combine = jsFunction('combine')
43+
# getConfig = jsFunction('getConfig')
44+
# extractShareComponents = jsFunction('extractShareComponents')
45+
# setRNG = jsFunction('setRNG')
46+
# str2hex = jsFunction('str2hex')
47+
# hex2str = jsFunction('hex2str')
48+
# random = jsFunction('random')
49+
# share = jsFunction('share')
50+
# newShare = jsFunction('newShare')
51+
#
52+
# # Test Functions
53+
# _reset = jsNeedless('_reset')
54+
# _isSetRNG = jsFunction('_isSetRNG')
55+
56+
# /* test-code */
57+
# // export private functions so they can be unit tested directly.
58+
# _reset: reset,
59+
# _padLeft: padLeft,
60+
# _hex2bin: hex2bin,
61+
# _bin2hex: bin2hex,
62+
# _hasCryptoGetRandomValues: hasCryptoGetRandomValues,
63+
# _hasCryptoRandomBytes: hasCryptoRandomBytes,
64+
# _getRNG: getRNG,
65+
# _isSetRNG: isSetRNG,
66+
# _splitNumStringToIntArray: splitNumStringToIntArray,
67+
# _horner: horner,
68+
# _lagrange: lagrange,
69+
# _getShares: getShares,
70+
# _constructPublicShareString: constructPublicShareString
71+
# /* end-test-code */

js2pysecrets/wrapper.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#! /usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
# vim: set et sw=4 fenc=utf-8:
4+
#
5+
# wrapper.py
6+
7+
"""
8+
This script is a revised version for calling Node.js to execute
9+
JavaScript using a wrapper to load the secrets.js package.
10+
This version handles multiple commands with a list, encodes the
11+
list to base36 for the CLI, and has improved error handling.
12+
"""
13+
14+
import json
15+
import os
16+
import subprocess
17+
import warnings
18+
19+
# Path to the Node.js wrapper script
20+
# JS_FILE_PATH = "./javascript/wrapper.js"
21+
22+
23+
def wrapper(input_data):
24+
"""
25+
Run a JavaScript function using the Node.js wrapper.
26+
27+
Args:
28+
input_data [list]: List of functions with arguments.
29+
30+
Returns:
31+
The result of the JavaScript function or None if there is an error.
32+
"""
33+
34+
# Get the directory where this Python script is located
35+
script_directory = os.path.dirname(os.path.realpath(__file__))
36+
37+
# Change the current working directory to the directory
38+
# containing the wrapper.js file
39+
os.chdir(os.path.join(script_directory, "..", "javascript"))
40+
41+
# Call the wrapper
42+
js_command = ["node", "wrapper.js", input_data]
43+
44+
try:
45+
# Run the command and capture the output and stderr
46+
result = subprocess.run(
47+
js_command,
48+
stdout=subprocess.PIPE,
49+
stderr=subprocess.PIPE,
50+
text=True,
51+
check=True,
52+
)
53+
54+
# Debugging statements to print stdout and stderr
55+
# print("Input data sent to JavaScript:", input_data)
56+
# print("stdout:", result.stdout)
57+
# print("stderr:", result.stderr)
58+
59+
try:
60+
# Attempt to load the entire stdout as JSON
61+
js_result = json.loads(result.stdout)
62+
# print('json.loads')
63+
64+
# Debugging stderr if it exists
65+
# if result.stderr:
66+
# print("JavaScript stderr:", result.stderr)
67+
68+
return js_result
69+
70+
except json.JSONDecodeError as e:
71+
# print("Python error decoding JSON:", e)
72+
# print("Raw stdout content:", result.stdout)
73+
warning_message = "Python error decoding JSON: " + str(e)
74+
warnings.warn(warning_message, category=Warning)
75+
return None
76+
77+
except FileNotFoundError:
78+
# Handle the case where Node.js is not installed
79+
print(
80+
"Error: Node.js is required. Please install Node.js to continue."
81+
) # pragma: no cover
82+
exit(1) # pragma: no cover
83+
except subprocess.CalledProcessError as e:
84+
# Print the error from the JavaScript script
85+
js_error = e.stderr.strip() # Use e.stderr instead of result.stderr
86+
# print("JavaScript error:", js_error)
87+
warning_message = "JavaScript error: " + js_error
88+
warnings.warn(warning_message, category=Warning)
89+
return None

tests/test_base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from js2pysecrets.base import NAME
1+
from js2pysecrets import NAME
22

33

44
def test_base():

0 commit comments

Comments
 (0)