In this blog post, you’ll learn how to write Java tests in Qorus. You’ll test the functionality developed in the “Poll SFTP Server For CSV Data And Import To DB” blog post series. You will begin by understanding the logic flow for the test and understand the code involved in each step.
Note: The complete test script is available in the examples/csv-sftp-to-db-import/ExampleImportCsvFileTest.java of the Qorus’ building blocks repo.
Logic Flow
1.Static Initialization
2. Set the job to run only on demand during the test
3. Create a test CSV file and put it on the SFTP server
4. Create a “RunJobResult” action and test for a COMPLETE status
5. Run the job and get the result
6. Check that a workflow order is created
7. Wait for order status
8. Get workflow order info and assert that it’s COMPLETE
9. Check if the test data has been added to the table
10. Check for duplicate file handling
4. Create a “RunJobResult” action and test for a COMPLETE status
5. Run the job and get the result
6. Check that a workflow order is created
7. Wait for order status
8. Get workflow order info and assert that it’s COMPLETE
9. Check if the test data has been added to the table
10. Check for duplicate file handling
11. Reset the job to run normally after the test completes
Note: Before you can run any Qorus tests, set the qorus-client.allow-test-execution to true in the /opt/qorus/etc/options file.
Qorus Imports and Classes
Import Statement | Description |
---|---|
qoremod.QorusClientBase.OMQ.$Constants | Qore constants can be imported using the special $Constants class name; constants in a Qore namespace are imported automatically into a special class with this name as "static final" fields |
qoremod.QorusClientBase.OMQ.Client.QorusSystemRestHelper | The QorusSystemRestHelper class makes it easy to talk to the Qorus REST API. When run a Qorus server it handles data serialization, deserialization and local authentication
It will use the network encryption key, if it's readable, to send an API request to the server in order to obtain a token with system permissions to execute Qorus API calls If the key is not readable, then it requires username and password configuration in the $OMQ_DIR/etc/options file at the qorus-client.client-url option |
qoremod.QorusClientBase.OMQ.QorusClientApi | QorusClientApi class implements the client interface to a Qorus server. |
qoremod.QorusClientBase.OMQ.UserApi.UserApi | UserApi is the main Qorus interface / utility class |
qoremod.ssh2.SSH2.SFTPClient | SFTPClient class allows Qorus code to communicate with SFTP servers. This is the object type that's returned from an sftp:// connection |
qoremod.SqlUtil.AbstractTable | The low-level parent class for the actual "Table" implementation in SqlUtil modules |
org.qore.jni.QoreClosureMarkerImpl | This marks a class that must have a call() method.
The call() method can be declared with any argument and return types and will be called when this object is converted to a Qore closure and the closure is called |
org.qore.jni.Hash | Java Hash class to make it easier to work with Qore hash data. |
1. Static Initialization
Static Initialization Initialize the QorusSystemRestHelper object in the class’s static initialization, as the global qrest variable in the Qorus’ Qore client is not available in Java
static { try { qrest = new QorusSystemRestHelper(); } catch (Throwable e) { throw new RuntimeException(e); } }
2. Set the job to run only on demand during the test
You can set the job to run only on demand during the job, with:
qrest.put("jobs/example-import-csv-file/setActive", new Hash() { { put("active", false); } });
3. Create A Test CSV File And Put It On The SFTP Server
Create a CSV file with test data:
String csv = ExampleImportCsvFileTest.getFileData(5);
String filename = String.format("StockReport-%s.csv", UUID.randomUUID().toString());
The getFileData above returns a string of CSV data that can be parsed by the interface
static String getFileData(int num_records) throws Throwable { return CsvHeader + Stream.generate(() -> getRecordString()).limit(num_records).collect(Collectors.joining()); }
The getRecordString method gets a single string for our CSV file with random data
static String getRecordString() { Hash prod = Products[ThreadLocalRandom.current().nextInt(0, Products.length)]; return String.format("%s,%s,\"%s\",%d,%d,%d,%s\n", Stores[ThreadLocalRandom.current().nextInt(0, Stores.length)], prod.getString("code"), prod.getString("desc"), ThreadLocalRandom.current().nextInt(0, 10), ThreadLocalRandom.current().nextInt(0, 10), ThreadLocalRandom.current().nextInt(0, 10), Instant.now().toString()); }
ℹ️ Note: Products and Stores are static variables defined like so:
public static Hash[] Products = { new Hash() { { put("code", "SV300S37A/120G"); put("desc", "Kingston SSDNow V300 120GB 7mm"); } }, new Hash() { { put("code", "SSDSC2BW120A401"); put("desc", "Intel 530 120GB SSD bulk"); } }, new Hash() { { put("code", "MZ-7PD256BW"); put("desc", "Samsung SSD840 256GB 7mm, Pro"); } }, };
public static String[] Stores = { "Václavské náměstí", "Náměstí Míru", "Malostranské náměstí", "Komenského náměstí", };
4. Create A "RunJobResult" Action And Test For A COMPLETE Status
The RunJobResult and Action classes are obtained from the QorusInterfaceTest package.
RunJobResult action = new RunJobResult ($Constants.StatComplete);
5. Run The Job And Get The Result
Before executing the job, get the current number of records in the table:
AbstractTable inventory_example = UserApi.getSqlTable("omquser", "inventory_example"); longnum_recs = inventory_example.rowCount();
Execute the action, get the result and store it in a Hash. You’ll utilise the exec method available by extending to the QorusJobTest class:
exec(action); Hash result = action.getJobResult();
6. Check That A Workflow Order Is Created
Get the job info with:
Now, check if one workflow order is created:
assertEq(1, jinfo.length, "check wf orders created");
7. Wait For Order Status
Wait for the order and get its status with:
void waitForStatus(long wfiid) throws Throwable { waitForStatus(wfiid, $Constants.StatComplete); } void waitForStatus(long wfiid, String status) throws Throwable { String stat; // poll the status ~4 times a second while (true) { Hash h = (Hash)qrest.get("orders/" + String.valueOf(wfiid)); stat = h.getString("workflowstatus"); if (stat.equals(status) || stat.equals($Constants.StatError)) { break; } // wait for status to change Thread.sleep(250); } // output the status System.out.println(String.format("workflow order ID %d has status %s", wfiid, stat)); // assert that the status is the desired status assertEq(stat, status, "wfiid " + String.valueOf(wfiid) + " has status " + status); }
Call the above waitForStatus method like so:
waitForStatus(jinfo[0].getLong("workflow_instanceid"));
8. Get Workflow Order Info And Assert That It's COMPLETE
You can get the workflow order and assert that it’s complete by:
// get workflow order info long wfiid = jinfo[0].getLong("workflow_instanceid"); Hash winfo = (Hash)qrest.get("orders/" + String.valueOf(wfiid)); // assert that it's COMPLETE assertEq($Constants.StatComplete, winfo.getString("workflowstatus"), "check order status");
9. Check If The Test Data Has Been Added To The Table
Check if the test data is added to the table, since we generated the CSV data with five records, we check if five new rows are added to the table:
assertEq(num_recs + 5, inventory_example.rowCount(), "check data imported in DB");
10. Check For Duplicate File Handling
Let’s resubmit the same file and repeat the process to test for duplicate file handling
putFileOnSftpServer(filename, csv); // create a "RunJobResult" action and test for a COMPLETE status action = new RunJobResult($Constants.StatComplete); // run the job and check the result exec(action); result = action.getJobResult(); // check job results that no new workflow order was created Hash[] jinfo2 = (Hash[])getJobResultHash(result.getLong("job_instanceid")).get("info"); assertEq(1, jinfo2.length, "check duplicate job result info length"); assertEq(wfiid, jinfo2[0].getLong("workflow_instanceid"), "check duplicate wf order ID"); assertTrue(jinfo2[0].getBool("duplicate"), "verify duplicate flag");
11. Reset Job To Run Normally After The Test Completes
Reset the job to run normally after the test completes with:
qrest.put("jobs/example-import-csv-file/setActive", new Hash() { { put("active", true); } });
Running The Test
Go to the Qorus IDE and in the interface hierarchy view, under the Tests file type, find the ExampleImportCsvFileTest.java (or the name of your test script ) test. Run the test by clicking on the play icon next to the test’s name.
You should see the following output after a successful test in VS Code at View → Output → Qorus Remote Development
... response: wrote 598 bytes of StockReport-ecd22aa9-bdc1-4bbc-bb9a-1c4b1baa8b02.csv to sftp://[your-server:port]
workflow order ID 4 has status COMPLETE
wrote 598 bytes of StockReport-ecd22aa9-bdc1-4bbc-bb9a-1c4b1baa8b02.csv to sftp://[your-server:port]
QUnit Test "example-import-csv-file" v<latest>
Ran 1 test case, 1 succeeded (13 assertions)
... status: FINISHED
... response: OpenJDK 64-Bit Server VM warning: Archived non-system classes are disabled because the java.system.class.loader property is specified (value = "org.qore.jni.QoreURLClassLoader"). To use archived non-system classes, this property must be not be set
... status: FINISHED
Test execution finished successfully (ID: 21)
Conclusion
In this blog post, you explored the steps involved in testing the functionality developed in the Poll SFTP Server For CSV Data And Import To DB blog post series using Java. The complete script is available in the examples/csv-sftp-to-db-import/ExampleImportCsvFileTest.java of the Qorus’ building blocks repo.