Test Larky locally
To try it out today, email us at [email protected]
How to run Larky code locally
This page demonstrates how to run Larky code locally on your PC without any interactions with VGS Vault. There is no need to prepare any YAML or to send the HTTPS request. This technique is good to be used when, for example, you have working Python code that does the encryption of the message body and you need to rewrite it to Larky. While rewriting, it gives a possibility of seeing the results of Larky encryption and comparing them with Python results.
In order to run Larky tests, you will need to have Java with JDK 16 (this guide will have you use jenv for this), you will need to install Maven, you will create a GitHub access token, and you will need to download the repo. Once the repo is downloaded, you will use JDK 16 (in the jenv) to run the Maven commands to compile, build, and run the tests.
Prepare your environment
Install
jenv
:
brew install jenv
echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(jenv init -)"' >> ~/.zshrc
exec $SHELL -l
Next, we use
brew
to search foropenjdk16
, install it, and add it to ourjenv
so that it has access to the right version of Java:
brew tap AdoptOpenJDK/openjdk
brew search /adoptopenjdk/
==> Casks
adoptopenjdk-jre adoptopenjdk12 adoptopenjdk14 adoptopenjdk16 ✔
adoptopenjdk-openj9 adoptopenjdk12-jre adoptopenjdk14-jre adoptopenjdk16-jre
adoptopenjdk-openj9-jre adoptopenjdk12-openj9 adoptopenjdk14-openj9 adoptopenjdk16-openj9
adoptopenjdk-openj9-jre-large adoptopenjdk12-openj9-jre adoptopenjdk14-openj9-jre adoptopenjdk16-openj9-jre
adoptopenjdk-openj9-large adoptopenjdk12-openj9-jre-large adoptopenjdk14-openj9-jre-large adoptopenjdk8 ✔
adoptopenjdk10 adoptopenjdk12-openj9-large adoptopenjdk14-openj9-large adoptopenjdk8-jre
adoptopenjdk11 ✔ adoptopenjdk13 adoptopenjdk15 adoptopenjdk8-openj9
adoptopenjdk11-jre adoptopenjdk13-jre adoptopenjdk15-jre adoptopenjdk8-openj9-jre
adoptopenjdk11-openj9 adoptopenjdk13-openj9 adoptopenjdk15-openj9 adoptopenjdk8-openj9-jre-large
adoptopenjdk11-openj9-jre adoptopenjdk13-openj9-jre adoptopenjdk15-openj9-jre adoptopenjdk8-openj9-large
adoptopenjdk11-openj9-jre-large adoptopenjdk13-openj9-jre-large adoptopenjdk15-openj9-jre-large adoptopenjdk9
adoptopenjdk11-openj9-large adoptopenjdk13-openj9-large adoptopenjdk15-openj9-large adoptopenjdk
brew install --cask adoptopenjdk16
jenv add /Library/Java/JavaVirtualMachines/adoptopenjdk-16.jdk/Contents/Home
jenv enable-plugin export
exec $SHELL -l
Install
maven
:
brew install maven
mkdir ~/.m2
touch ~/.m2/settings.xml
# You will need to copy the contents of the settings.xml
# from GitHub to your repo, placing your credentials in it
Clone the larky repo and build it with
jenv maven
:
git clone https://github.com/verygoodsecurity/starlarky.git
cd starlarky
jenv exec mvn clean package install
This is it! Now you're ready to run the Larky test:
vim larky/src/test/resources/quick_tests/test_<name>.star
jenv exec mvn -Dtest='LarkyQuickTests*' -Dlarky.quick_test=test_<name>.star test -pl larky
Writing a Larky Test
Writing a Larky test is simple. There are a few things that each Larky Test file will need.
Filename. Test files should be named
test_<name>.star
. The directorylarky/src/test/resources
contains the directories in which you place the test file. Testing functionality for things in the Larkystdlib
belongs in thestdlib_tests
folder, testing functionality for vendor libraries goes in thevendor_tests
folder, and anything else you need to make a quick test for goes in thequick_tests
folder.You will need to import the
unittest
andasserts
libraries.You will need to create an actual function to test the functionality you want to test. This should be concluded with an assertion.
You will need the
_testsuite()
and_runner
. The names of the functions that you want to run to test must be placed into the test suite via_suite.addTest(unittest.FunctionTestCase(<function>)
. If you do not do this, and simply declare a functiondef test():
and later call it withtest()
at the end of the file, it will not run the actual test. This can cause it to appear as though the test is passing when in fact it is not being run, and makes false positives for passing when the test may not actually pass.
You can reference this basic “Hello, World!” Larky test as an example:
load("@stdlib//unittest", "unittest")
load("@vendor//asserts", "asserts")
def test():
a = 'Hello, '
b = 'World!'
c = a + b
asserts.assert_that(c).is_equal_to('Hello, World!')
def _testsuite():
_suite = unittest.TestSuite()
_suite.addTest(unittest.FunctionTestCase(test))
return _suite
_runner = unittest.TextTestRunner()
_runner.run(_testsuite())
Saving this in the quick_tests
folder as test_hello.star
you can run it with the following:
jenv exec mvn -Dtest='LarkyQuickTests*' -Dlarky.quick_test=test_hello.star test -pl larky
Real Larky test example
Below are the code blocks in Python and Larky, respectively:
import requests
import binascii
import base64
from Crypto.Hash import SHA512
from Crypto.Hash import HMAC
method = 'POST'
uri = '/api/v3/transaction/rmscloudsimulator/debit'
headers = { 'Content-Type': 'application/json; charset=utf-8',
'Date': 'Wed, 22 Dec 2021 10:52:03 UTC',
'Shared-Secret': '5Q3E4yvQzDHU7dM54R46wzaSPD7nnP'}
body = '{"merchantTransactionId":"2019-09-02-0004","amount":"9.99","currency":"EUR"}'
content_type = headers['Content-Type']
date = headers['Date']
secret = headers['Shared-Secret']
body_utf8 = bytes(body, encoding="utf-8")
h = SHA512.new()
h.update(body_utf8)
signature = h.hexdigest()
print('>>> Signature: ', signature)
hmac_input = "\n".join(
[
method,
signature,
content_type,
date,
uri
]
)
hmac_input = bytes(hmac_input, encoding="utf-8")
secret = str.encode(secret)
hash_value = HMAC.new(secret, digestmod=SHA512)
hash_value.update(hmac_input)
result = base64.b64encode(hash_value.digest()).decode("utf-8")
print('>>> Result :', result)
{String.raw`load("@stdlib//unittest", "unittest")
load("@vendor//asserts", "asserts")
load("@stdlib//json", "json")
load("@stdlib//base64", base64="base64")
load("@vendor//Crypto/Hash/HMAC", HMAC="HMAC")
load("@vendor//Crypto/Hash/SHA512", SHA512="SHA512")
def test():
# body = json.loads(input.body())
method = 'POST'
uri = '/api/v3/transaction/rmscloudsimulator/debit'
headers = {'Content-Type': 'application/json; charset=utf-8',
'Date': 'Wed, 22 Dec 2021 10:52:03 UTC',
'Shared-Secret': '5Q3E4yvQzDHU7dM54R46wzaSPD7nnP'}
body = '{"merchantTransactionId":"2019-09-02-0004","amount":"9.99","currency":"EUR"}'
content_type = headers['Content-Type']
date = headers['Date']
secret = headers['Shared-Secret']
body_utf8 = bytes(body, encoding="utf-8")
signature = SHA512.new(body_utf8).hexdigest()
print('\n\n >>> Signature: ', signature, '\n')
hmac_input = "\n".join(
[
method,
signature,
content_type,
date,
uri
]
)
hmac_input = bytes(hmac_input, encoding="utf-8")
secret = bytes(secret, encoding="utf-8")
hash_value = HMAC.new(secret, hmac_input, digestmod=SHA512).digest()
result = base64.b64encode(hash_value)
print('\n\n >>> hexOutput: ', result, '\n')
def _testsuite():
_suite = unittest.TestSuite()
_suite.addTest(unittest.FunctionTestCase(test))
return _suite
_runner = unittest.TextTestRunner()
_runner.run(_testsuite())
`}
Execution results:
python sample.py
>>> Signature: efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
>>> Result : HFgpaSQVGq9o7WQyQGAevpMtdVsBsw79aZOE2V9VqVUdaziNB+NegwSLk4gqBviOHB7drQYYP3HnS3zDyd9IWg==
jenv exec mvn -Dtest='LarkyQuickTests*' -Dlarky.quick_test=test_sha512_HMAC.star test -pl larky
...
0408 14:09:01.932 INFO: /Users/oneshot/Downloads/starlarky/larky/src/test/resources/quick_tests/test_sha512_HMAC.star:28:10:
.
>>> Signature: efe0b7cd39d6904dc90924b1a89629b14f11082ed2178cff562364ca0172318e1535bb8766fbe66e8cc44d311eba806349bfe185607eca12d9d0f377a03ee617
.
0408 14:09:01.944 INFO: /Users/oneshot/Downloads/starlarky/larky/src/test/resources/quick_tests/test_sha512_HMAC.star:46:10:
.
>>> hexOutput: HFgpaSQVGq9o7WQyQGAevpMtdVsBsw79aZOE2V9VqVUdaziNB+NegwSLk4gqBviOHB7drQYYP3HnS3zDyd9IWg==
.
Running test suite (1 tests to run):
Testing test >>> SUCCESS
...
As you see both Signature
and hexOutput
coincide, which means both code samples work in the same way. The Larky code is ready to be placed into YAML.
Last updated