CLIENT_UNIT, META_CLIENT_IP => STRING_SESSION_HOME, META_SYSTEM_NOTES => STRING_ORIGIN_UT, META_TEMPLATE => TEMPLATE_CLASS_TEST_MONGO ]; // meta data for admin requests static::$adminMeta = [ META_CLIENT => CLIENT_UNIT, META_CLIENT_IP => STRING_SESSION_HOME, META_SYSTEM_NOTES => STRING_ORIGIN_UT ]; } /** * setUp() -- unit test reserved method * * the setUp method is executed prior to each test, executing the following tasks: * * 1. validate that the meta data persisted across tests * 2. validate that we successfully created a write-broker client * 3. validate that we successfully created a read-broker client * 4. validate the successful instantiation of a factory-class and assign to the widget static * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 07-31-17 mks original coding * 12-10-18 mks DB-71: support for audit/journal testing * */ protected function setUp() { $errors = array(); // validate the meta data for the pending query $this->assertTrue(!empty(static::$meta), __FILE__ . COLON . __LINE__ . COLON . ERROR_DATA_META_404); $this->assertTrue(is_array(static::$meta), __FILE__ . COLON . __LINE__ . COLON . ERROR_META_INVALID_FORMAT_ARRAY); $this->assertTrue(array_key_exists(META_TEMPLATE, static::$meta), __FILE__ . COLON . __LINE__ . COLON . ERROR_DATA_META_KEY_404 . META_TEMPLATE); // create the write broker client $this->writeBrokerClient = new gacBrokerClient(BROKER_QUEUE_W, basename(__FILE__) . COLON . __METHOD__ . COLON . __LINE__); $this->assertTrue($this->writeBrokerClient->status, __METHOD__ . __LINE__ . ERROR_BROKER_QUEUE_DECLARE . BROKER_QUEUE_W); // create the read broker client $this->readBrokerClient = new gacBrokerClient(BROKER_QUEUE_R, basename(__FILE__) . COLON . __METHOD__ . COLON . __LINE__); $this->assertTrue($this->readBrokerClient->status, __METHOD__ . __LINE__ . ERROR_BROKER_QUEUE_DECLARE . BROKER_QUEUE_R); // create the admin broker client $this->adminOutClient = new gacBrokerClient(BROKER_QUEUE_AO, basename(__FILE__) . COLON . __METHOD__ . COLON . __LINE__); $this->assertTrue($this->adminOutClient->status, __FILE__ . COLON . __LINE__ . COLON . ERROR_BROKER_QUEUE_DECLARE . BROKER_QUEUE_AO); // instantiate a new widget $objFactory = new gacFactory(static::$meta, FACTORY_EVENT_NEW_CLASS, '', $errors); $this->assertTrue($objFactory->status, __FILE__ . COLON . __LINE__ . COLON . ERROR_FAILED_TO_INSTANTIATE . static::$meta[META_TEMPLATE]); // extract and assign the factory widget and deallocate the factory static::$widget = $objFactory->widget; if (is_object($objFactory)) $objFactory->__destruct(); unset($objFactory); // ensure that caching, auditing, and journaling are enabled for the class and that deletes are set to false $this->assertTrue(static::$widget->useCache, __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . STRING_CACHE); $this->assertGreaterThan(0, static::$widget->useAuditing, __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_NON_ZERO_INT . static::$widget->useAuditing); $this->assertTrue(static::$widget->useJournaling, __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . STRING_JOURNAL); $this->assertFalse(static::$widget->useDeletes, __FILE__ . COLON . __LINE__ . ERROR_UT_EXPECTING_FALSE . STRING_DELETE); } /** * tearDown() -- unit test method * * this is the (reserved) tearDown method which is executed at the end of every test. * the method deletes the current broker client. * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 07-27-17 mks original coding * */ protected function tearDown() { /** @noinspection PhpUndefinedMethodInspection */ if (is_object($this->readBrokerClient)) $this->readBrokerClient->__destruct(); unset($this->readBrokerClient); if (is_object($this->writeBrokerClient)) $this->writeBrokerClient->__destruct(); unset($this->writeBrokerClient); if (is_object(static::$widget)) { /** @noinspection PhpUndefinedMethodInspection */ static::$widget->__destruct(); } static::$widget = null; } /** * testInsert() -- unit test method * * this unit test method is a simple test consisting of the following: * * 1. validate that we have a valid widget object * 2. validate that we were able to create a test record of 1 record * -- submit the create-event to the broker * 3. validate that the create-event was successful and returned 1 cache-key * -- fetch the cached record * 4. validate that the integer, float, bool and string values are the same * 5. validate that the guid, status and cData values were inserted * 6. validate the guid * 7. validate the status (active) * 8. validate the date as an integer * * Assumptions: * ------------ * That we know the test class being used supports caching. * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 07-31-17 mks CORE-486: original coding * 12-11-18 mks DB-71: additional queries for testing audit/journaling * 03-14-19 mks DB-116: updated for the new cacheMapping * */ public function testOneInsert() { $testRecord = null; $this->assertTrue(is_object(static::$widget), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_WIDGET_404); // create one record $testRecord = static::$widget->template->buildTestData(1); $this->assertTrue(is_array($testRecord), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . 'is_array($testRecord)'); $this->assertEquals(1, count($testRecord), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, count($testRecord))); // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_CREATE, BROKER_DATA => $testRecord, BROKER_META_DATA => static::$meta ]; // build the request payload and submit the request to the write broker $payload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); // validate the response payload $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_CREATE, $response[PAYLOAD_STATE])); $this->assertEquals(STATE_SUCCESS, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_STRING_MISMATCH, STATE_SUCCESS, $response[PAYLOAD_STATE])); $this->assertEquals(1, count($response[PAYLOAD_RESULTS]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, count($response[PAYLOAD_RESULTS]))); // get the cache-key from the response payload $cKey = $response[PAYLOAD_RESULTS][0]; $cacheRecord = gasCache::get($cKey); $cacheRecord = json_decode(gzuncompress($cacheRecord), true); // test the four primary values (int, float, bool, string) of the pre-saved record and // the post-saved cached record $this->assertEquals($testRecord[0][CM_TST_FIELD_TEST_INT], $cacheRecord[0][CM_TST_FIELD_TEST_INT], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_INT)); $this->assertEquals($testRecord[0][CM_TST_FIELD_TEST_STRING], $cacheRecord[0][CM_TST_FIELD_TEST_STRING], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_STRING)); $this->assertEquals($testRecord[0][CM_TST_FIELD_TEST_DOUBLE], $cacheRecord[0][CM_TST_FIELD_TEST_DOUBLE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_DOUBLE)); $this->assertEquals($testRecord[0][CM_TST_FIELD_TEST_BOOL], $cacheRecord[0][CM_TST_FIELD_TEST_BOOL], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_BOOL)); // validate that we have dateCreated, key and status fields which were inserted by the framework $this->assertTrue(array_key_exists(CM_TST_FIELD_TEST_CDATE, $cacheRecord[0]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_FIELD_TEST_CDATE, RESOURCE_CACHE)); $this->assertTrue(array_key_exists(CM_TST_TOKEN, $cacheRecord[0]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_TOKEN, RESOURCE_CACHE)); $this->assertTrue(array_key_exists(CM_TST_FIELD_TEST_STATUS, $cacheRecord[0]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_FIELD_TEST_STATUS, RESOURCE_CACHE)); // validate that these fields have content of the correct type or that the literals match // validate the token guid $this->assertTrue(validateGUID($cacheRecord[0][CM_TST_TOKEN]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_TOKEN)); // validate the status value $this->assertEquals(STATUS_ACTIVE, $cacheRecord[0][CM_TST_FIELD_TEST_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_FIELD_TEST_STATUS)); // validate the createdData type $this->assertTrue(is_int($cacheRecord[0][CM_TST_FIELD_TEST_CDATE]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_FIELD_TEST_CDATE)); // to prepare for the next unit test, let's store some data about the newly-created record for compare/contrast // start by fetching an unfiltered version of the new record $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [$cacheRecord[0][CM_TST_TOKEN]]]]]; $tmpMeta = static::$meta; $tmpMeta[META_DONUT_FILTER] = 1; $payload = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $tmpMeta ]; $payload = gzcompress(json_encode($payload)); $response = $this->readBrokerClient->call($payload); $results = json_decode(gzuncompress($response), true); static::$testRecord = $results[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0]; // store a copy of the new record static::$checksum = md5(base64_encode(json_encode(static::$testRecord))); // calculate & store the checksum // save the cKey value as we're going to use this record in subsequent tests static::$cKey = $cKey; } /** * testInsertAudit() -- unit test method * * This unit test will validate that the previous test, for creating/inserting a new record, will proc an audit * event. We've saved a copy, in the stored format, of the record just created as static member and we've * calculated the checksum of the encoded record snapshot, also in a member variable, prior to calling this method. * * The first assertions in this test confirm that the requisite saved data exists and is in the correct format. * * Next, we fetch the cached record so that we can extract both the GUID of newly-created record, and the record's * event GUID. Validate both as GUIDs. * * Then we build a query to fetch the audit record from admin. We test that we were able to fetch the record and * then we calculate the checksum of the record snapshot and compare it to the stored checksum. * * If all assertion complete successfully, then we store the token GUID for the audit record as a member static * to use in the next test. * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 12-11-18 mks DB-77: original coding * */ public function testInsertAudit() { // pause to allow disk writes to catch up sleep(1); $this->assertNotEmpty(static::$cKey, __FILE__ . COLON . __LINE__ . COLON . 'lost the cache key'); $this->assertNotEmpty(static::$testRecord, __FILE__ . COLON . __LINE__ . COLON . 'lost the record'); $this->assertTrue(is_array(static::$testRecord), __FILE__ . COLON . __LINE__ . COLON . 'munged the record'); $this->assertNotEmpty(static::$checksum, __FILE__ . COLON . __LINE__ . COLON . 'lost the cksum'); // fetch the cached record $cacheRecord = gasCache::get(static::$cKey); $this->assertNotNull($cacheRecord, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL); $cacheRecord = json_decode(gzuncompress($cacheRecord), true); $cacheRecord = $cacheRecord[0]; // extract the token for the cached record, and the eventGUID to fetch the audit record that should // have been created in the last test... $recordToken = $cacheRecord[CM_TST_TOKEN]; $eventToken = $cacheRecord[CM_TST_EVENT_GUID]; $this->assertTrue(validateGUID($recordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . $recordToken); $this->assertTrue(validateGUID($eventToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . $eventToken); // save these two tokens for the next test static::$eventRecordToken = $eventToken; static::$testRecordToken = $recordToken; // build the query to fetch the audit record from the admin service $query = [ AUDIT_RECORD_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [ $recordToken ]]], DB_EVENT_GUID => [ OPERAND_NULL => [ OPERATOR_EQ => [ $eventToken ]]], OPERAND_AND => null ]; static::$adminMeta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $request = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => static::$adminMeta ]; $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_REMOTE_FETCH, $response[PAYLOAD_STATE])); $auditData = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0]; // we've validated that we created an audit record -- test that it's the correct record // because the order of the fields may have changed, we validate by looking at the record GUID values $ext = COLLECTION_MONGO_TEST_EXT; $t1 = static::$testRecord[(DB_TOKEN . $ext)]; $t2 = $auditData[(AUDIT_RECORD_TOKEN . COLLECTION_MONGO_AUDIT_EXT)]; $this->assertEquals($t1, $t2, __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_VALS_NOT_EQUAL, 'new record checksum', 'audit record checksum')); $this->assertTrue(validateGUID($auditData[DB_TOKEN . COLLECTION_MONGO_AUDIT_EXT]), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . $auditData[DB_TOKEN . COLLECTION_MONGO_AUDIT_EXT]); // save the audit record token static::$auditRecordToken = $auditData[DB_TOKEN . COLLECTION_MONGO_AUDIT_EXT]; } /** * testInsertJournal() -- unit test method * * This test takes information saved in the previous method (hence the assertions at the start of the method), * and uses this data to fetch the journal record from the database. * * We're going to query the db on three elements: * * 1. The namaste-record token * 2. The event GUID generated during the unit-test insert (one record) test * 3. The audit-record guid generated from the insert operation * * Even if there is more than one journal record associated with the namaste record, using the audit record key * (as well as the event guid) and the record GUID, forces the query to return, at most, a single record. * And it validates that we've successfully saved the correct journal record to the database using that query * instead of multiple assertions. * * We assert that the fetch request was successful and that we returned only one record in the query. The method * then extracts the data from the return payload and assigns it to a member variable for the next unit test. * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 12-11-18 mks DB-77: original coding * */ public function testInsertJournal() { // pause to let disk-writes catch-up sleep(1); $this->assertTrue(validateGUID(static::$auditRecordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(validateGUID(static::$eventRecordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . static::$eventRecordToken); $this->assertTrue(validateGUID(static::$testRecordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . static::$testRecordToken); $this->assertNotEmpty(static::$cKey, __FILE__ . COLON . __LINE__ . COLON . 'lost the cache key'); $testRecord = gasCache::get(static::$cKey); $this->assertNotNull($testRecord, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL); // implicitly test that we created the journal record off the insert event by querying the database by the // data record token, the event guid, and the audit token together. This way, if the record has more than // one journal record associated with the record guid, we're still filtering by the event guid. // build the fetch query for the journaling collection $query = [ JOURNAL_AUD_TOK => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$auditRecordToken ]]], JOURNAL_SYSEV_TOK => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$eventRecordToken]]], JOURNAL_RECORD_GUID => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$testRecordToken]]], OPERAND_AND => null ]; $tmpMeta = static::$adminMeta; $tmpMeta[META_TEMPLATE] = TEMPLATE_CLASS_JOURNAL; $payload = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $tmpMeta ]; $request = gzcompress(json_encode($payload)); $response = $this->adminOutClient->call($request); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_FETCH, $response[PAYLOAD_STATE])); $this->assertEquals(1, count($response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, count($response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS]))); // load the journal record into a static member for the next test static::$journalRecord = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0]; } /** * testInsertJournalRecovery() -- unit test method * * This method tests that we're able to "recover" the previous operation using the audit record that was created * when we created the new namaste record. Because the previous operation was a "CREATE", the rollback event * stored in journaling should remove the record from existence. Because the test class supports soft-deletes, * that means that the record will not actually be deleted but, instead, have it's status set to DELETED. * * This means that we're also, albeit indirectly, testing the fetch-broker's ability to process a query that * executes successfully but returns no records. * * ASSERTIONS: * ----------- * Validate that we have stored the the necessary data in member statics -- ensure that they're valid and available * Restore the audit record (by invoking the recovery event) and test that the event completed successfully * Fetch the original record and validate that the record was "restored" (deleted) successfully * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 12-11-18 mks DB-77: original coding * */ public function testInsertJournalRecovery() { $this->assertTrue(!empty(static::$journalRecord), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, 'journalRecord', 'class member')); $this->assertTrue(validateGUID(static::$auditRecordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(validateGUID(static::$testRecordToken), __FILE__ . COLON . __LINE__ . COLON . ERROR_INVALID_GUID . static::$testRecordToken); $this->assertNotEmpty(static::$cKey, __FILE__ . COLON . __LINE__ . COLON . 'lost the cache key'); $testRecord = gasCache::get(static::$cKey); $this->assertNotNull($testRecord, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL); // build the recovery query which should flag the targeted record into a deleted state $query = [ STRING_KEY => static::$auditRecordToken ]; $meta = static::$adminMeta; $meta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $request = [ BROKER_REQUEST => BROKER_REQUEST_AUDIT_RESTORE, BROKER_DATA => $query, BROKER_META_DATA => $meta ]; $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_AUDIT_RESTORE, $response[PAYLOAD_STATE])); // if we're still executing, that the record was "restored" --- attempt to pull the record $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [static::$testRecordToken ]]]]; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => static::$meta ]; $payload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertEquals(STATE_NOT_FOUND, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . sprintf(ERROR_UT_VALS_NOT_EQUAL, STATE_NOT_FOUND, $response[PAYLOAD_STATE])); } /** * testMultipleInserts() -- unit test method * * This method functions almost identically to the previous test method except, in this method, we're testing * batch-inserts of between 50 and 100 records. * * 1. validate that the widget object is still viable * -- generate a random value between 50 and 100 * -- generate that number of random test records * 2. validate that we were able to generate the correct number of random records * -- build, submit and evaluate the broker-event request return data * 3. validate that the event returned the same number of records processed (count) * -- generate a random number between 0 and the number of records generated * -- fetch that record from cache and unpack the cache record * 4. validate that the base values are the same (request -> insert -> cache -> test) * 5. validate that we added createdData, guid, and status to the record * 6. validate the type of the added data elements * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 08-01-17 mks original coding * 11-28-17 mks CORE-635: updated the unit test for the new cache-handling method * 03-15-19 mks DB-116: refactored for broker-level cacheMapping * */ public function testMultipleInserts() { $testRecord = null; $this->assertTrue(is_object(static::$widget), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_WIDGET_404); // create a random number of records between 50 and 100 $recCount = mt_rand(50,100); $testRecord = static::$widget->template->buildTestData($recCount); $this->assertTrue(is_array($testRecord), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . 'is_array($testRecord)'); $this->assertEquals($recCount, count($testRecord), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, count($testRecord))); // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_CREATE, BROKER_DATA => $testRecord, BROKER_META_DATA => static::$meta ]; // build the request payload and submit the request to the write broker $payload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], (__FILE__ . COLON) . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . PAYLOAD_STATUS); // validate that the count of inserted records matches the number of records, or cache keys, returned // first, fetch the referenced item from cache $guidPayload = json_decode(gzuncompress(gasCache::get($response[PAYLOAD_RESULTS][0])), true); $this->assertTrue(is_array($guidPayload), basename(__METHOD__) . AT . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, 'guidPayload')); $this->assertEquals(count($guidPayload), $recCount + 1, basename(__METHOD__) . AT . __LINE__ . COLON . sprintf(ERROR_UT_VALS_NOT_EQUAL, count($guidPayload), ($recCount + 1))); // validate the checksum for the cached-key list $checksum = $guidPayload[STRING_CHECKSUM]; unset($guidPayload[STRING_CHECKSUM]); $newCSum = md5(gzcompress(json_encode($guidPayload))); $this->assertEquals($checksum, $newCSum, (__FILE__ . COLON . __LINE__ . COLON) . ERROR_UT_CHECKSUM); // fetch a random record from cache $rr = mt_rand(0, ($recCount - 1)); $rec = $guidPayload[$rr]; $recCacheGUID = $rec[STRING_KEY]; $this->assertTrue(validateGUID($recCacheGUID), basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_INVALID_GUID . $recCacheGUID); // compare the random record base values to the same record still stored in the array we submitted to the broker $this->assertEquals($testRecord[$rr][CM_TST_FIELD_TEST_INT], $rec[CM_TST_FIELD_TEST_INT], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_INT)); $this->assertEquals($testRecord[$rr][CM_TST_FIELD_TEST_STRING], $rec[CM_TST_FIELD_TEST_STRING], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_STRING)); $this->assertEquals($testRecord[$rr][CM_TST_FIELD_TEST_DOUBLE], $rec[CM_TST_FIELD_TEST_DOUBLE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_DOUBLE)); $this->assertEquals($testRecord[$rr][CM_TST_FIELD_TEST_BOOL], $rec[CM_TST_FIELD_TEST_BOOL], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_BOOL)); // validate that we have dateCreated, key and status fields which were inserted by the framework $this->assertTrue(array_key_exists(CM_TST_FIELD_TEST_CDATE, $rec), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_FIELD_TEST_CDATE, RESOURCE_CACHE)); $this->assertTrue(array_key_exists(CM_TST_TOKEN, $rec), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_TOKEN, RESOURCE_CACHE)); $this->assertTrue(array_key_exists(CM_TST_FIELD_TEST_STATUS, $rec), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_404, CM_TST_FIELD_TEST_STATUS, RESOURCE_CACHE)); // validate that these fields have content of the correct type or that the literals match // validate the token guid $this->assertTrue(validateGUID($rec[CM_TST_TOKEN]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_TOKEN)); // validate the status value $this->assertEquals(STATUS_ACTIVE, $rec[CM_TST_FIELD_TEST_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_FIELD_TEST_STATUS)); // validate the createdData type $this->assertTrue(is_int($rec[CM_TST_FIELD_TEST_CDATE]), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_FIELD_VALUE, CM_TST_FIELD_TEST_CDATE)); } /** * testNegativeInsertCountExceedsLimit() * * this is a negative unit test in that we're intentionally provoking a failure in the form of a request that the * number of new records to create exceeds the declared maximum number of allowable records to be processed in a * single event request. * * Pull the current record limit from the configuration and then generate a payload of records that is equal to * the record limit, plus one. * * Create the payload for the event, submit the request, and then test out the return payload results against * the expectations. * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 09-11-17 mks CORE-501: original coding * */ public function testNegativeInsertCountExceedsLimit() { $testRecords = null; $this->assertTrue(is_object(static::$widget), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_WIDGET_404); // create more records than the current configuration allows $qrl = intval(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_QUERY_RECORD_LIMIT]); $recCount = 1 + $qrl; $testRecords = static::$widget->template->buildTestData($recCount); $this->assertTrue(is_array($testRecords), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_TRUE . 'is_array($testRecord)'); $this->assertEquals($recCount, count($testRecords), __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, count($testRecords))); // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_CREATE, BROKER_DATA => $testRecords, BROKER_META_DATA => static::$meta ]; // build the request payload and submit the request to the write broker $payload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); // test that the event was rejected with the expected state value $this->assertFalse($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_FALSE . BROKER_REQUEST_CREATE); $this->assertEquals(STATE_DATA_ERROR, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_STRING_MISMATCH, STATE_DATA_ERROR, $response[PAYLOAD_STATE])); } /** * testUpdateOne() -- unit test method * * this unit test will pull the record we saved in testOneInsert() -- because this was a recently saved record, * we know the primary key value and can launch a query to cache to fetch the record. * * We'll pull the token value from the cache record and compare it to the static::$cKey to ensure it's the same and * use the value to build the query for the update event. * * Create a new string value to replace the string value in the target record and use this value in the update * query. * * Build the event-request payload and submit it, and evaluate the return. * * 1. Validate that the widget class is still active * 2. Validate that the cache key is available via a local property * 3. Validate that we were able to fetch the cached record by the key * -- Build the event-query payload * -- Submit and unpack the broker response * 4. Validate that the response reported a successful completion * -- Fetch the cached record which should have been updated on a successful update db event * 5. Validate that the cached record now has the updated string stored * * Note that we're implicitly validating the cache-update process by going to cache for our records instead * of submitting queries to the read-broker. * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 08-01-17 mks CORE-486: original coding * 01-03-19 mks DB-79: audit-testing support -- refactored to grab a random record b/c we "deleted" the * record in a previous method testing journal recovery on a create operation * */ public function testUpdateOne() { $testRecord = null; // build a simple update query by pulling an active record that doesn't have the jayne string $query = [TEST_FIELD_TEST_STRING => [ OPERAND_NULL => [ OPERATOR_DNE => [static::$jayne]]]]; // we need to fetch the "before" version of the record before it's updated for testing the audit later $adminMeta = static::$adminMeta; $adminMeta[META_DONUT_FILTER] = 1; $adminMeta[META_SKIP_AUDIT] = 1; $adminMeta[META_TEMPLATE] = TEMPLATE_CLASS_TEST_MONGO; $adminMeta[META_LIMIT] = 1; $adminMeta[META_EVENT_GUID] = guid(); $ext = '_tst'; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $adminMeta ]; $eventPayload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], (basename(__FILE__) . COLON . __LINE__ ) . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_FETCH, $response[PAYLOAD_STATE])); $recCount = intval($response[PAYLOAD_RESULTS][STRING_QUERY_DATA][STRING_REC_COUNT_RET]); $this->assertEquals(1, $recCount, (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 1, $recCount) . MONGO_STRING_COUNT); $testRecord = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertEquals(STATUS_ACTIVE, $testRecord[(DB_STATUS . $ext)], (basename(__FILE__) . COLON . __FILE__) . COLON . sprintf(ERROR_UT_VALS_NOT_EQUAL, STATUS_ACTIVE, $testRecord[(DB_STATUS . $ext)])); $this->assertArrayHasKey((STRING_TOKEN . $ext), $testRecord, (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_FIELD_404, STRING_TOKEN, 'testRecord')); $guid = $testRecord[(STRING_TOKEN . $ext)]; $this->assertTrue(validateGUID($guid), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . $guid); // we have a copy of the record we're going to update - build the update query $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [$guid]]]]; // quick validate that we're working with a new string $testString = ''; if (array_key_exists((TEST_FIELD_TEST_STRING . $ext), $testRecord)) $testString = $testRecord[(TEST_FIELD_TEST_STRING . $ext)]; elseif (array_key_exists(TEST_FIELD_TEST_STRING, $testRecord)) $testString = $testRecord[TEST_FIELD_TEST_STRING]; $this->assertNotEquals(static::$jayne, $testString, (__FILE__ . COLON . __LINE__) . COLON . sprintf(ERROR_UT_STRING_MATCH, 'jayne', (TEST_FIELD_TEST_STRING . $ext))); // build the update query $update = [ CM_TST_FIELD_TEST_STRING => static::$jayne ]; // build the broker-update-event payload $payload = [ STRING_QUERY_DATA => $query, STRING_UPDATE_DATA => $update ]; $eventGUID = guid(); // generate a known event guid for audit tracking $meta = static::$meta; $meta[META_EVENT_GUID] = $eventGUID; // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_UPDATE, BROKER_DATA => $payload, BROKER_META_DATA => $meta ]; // submit the request and process the result payload $eventPayload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); // verify that the update was successful $this->assertTrue($response[PAYLOAD_STATUS], (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_EXPECTING_TRUE . PAYLOAD_STATUS); // we're no longer testing the cache-record since we disabled caching for this event // $newRecord = json_decode(gzuncompress(gasCache::get($guid)), true); // $newString = $newRecord[0][CM_TST_FIELD_TEST_STRING]; // $this->assertEquals(static::$jayne, $newString, (__FILE__ . COLON . __LINE__) . COLON . sprintf(ERROR_UT_STRING_MISMATCH, static::$jayne, $newString)); // save the guids and a copy of the original record in the class statics static::$eventRecordToken = $eventGUID; static::$testRecordToken = $guid; static::$originalTestRecord = $testRecord; } /** * testUpdateAudit() -- unit test method * * This unit test is predicated on the previous method where we fetched a random, valid record that hasn't had the * string value changed and, was updated to change the string value stored. * * We've previously stored the original record prior to update. In this method, we'll fetch the audit record * based on the record and event GUIDs and validate that we created an audit record off the update event. * * Next, we extract the audit record token and store it as a class static as well as the entire audit record. * * This sets us up for the next method which tests that a journal record was created, also off the update event, * that's linked-back to the audit record. * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 01-04-19 mks DB-79: original coding * */ public function testUpdateAudit() { // verify that we have a eventGUID so we can fetch the audit token, and verify that we have a copy of // the original record before it was updated in the previous method $this->assertTrue(validateGUID(static::$eventRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$eventRecordToken); $this->assertTrue(validateGUID(static::$testRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$testRecordToken); $this->assertTrue(is_array(static::$originalTestRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'originalTestRecord'); // fetch the audit record based on the record and event guids, testing that we successfully created an audit // record off the update operation in the previous method $adminMeta = static::$adminMeta; $adminMeta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $query = [ AUDIT_RECORD_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$testRecordToken ]]], DB_EVENT_GUID => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$eventRecordToken ]]], OPERAND_AND => null ]; $request = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $adminMeta ]; sleep(1); $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_REMOTE_FETCH, $response[PAYLOAD_STATE])); static::$auditRecordToken = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0][STRING_TOKEN . COLLECTION_MONGO_AUDIT_EXT]; $this->assertNotEmpty(static::$auditRecordToken, (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'audit record token'); $this->assertTrue(validateGUID(static::$auditRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); static::$auditRecord = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertTrue(is_array(static::$auditRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_GENERIC_FAIL . 'failed to fetch audit record'); } /** * testUpdateJournal() -- unit test method * * This function continues the previous two methods of testing by asserting that the previous update event created * a journal record. We're only testing that the journal record exists, and through the query itself, validates * the back-linking to the audit record via the eventGUID and the auditGUID. * * Once we've confirmed that the journal record was successfully created, then we'll store the journal token and * the journal record for the final method/test: restoring the record to it's previous state. * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 01-04-19 mks DB-78: original coding * */ public function testUpdateJournal() { // validate that the carry-over data exists $this->assertTrue(validateGUID(static::$eventRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$eventRecordToken); $this->assertTrue(validateGUID(static::$testRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$testRecordToken); $this->assertTrue(is_array(static::$originalTestRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'originalTestRecord'); $this->assertTrue(validateGUID(static::$auditRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(is_array(static::$auditRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'auditRecord'); // fetch the journal record based on the event guid and the audit guid $query = [ JOURNAL_AUD_TOK => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$auditRecordToken ]]], DB_EVENT_GUID => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$eventRecordToken]]], OPERAND_AND => null ]; $adminMeta = static::$adminMeta; $adminMeta[META_TEMPLATE] = TEMPLATE_CLASS_JOURNAL; $request = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $adminMeta ]; $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_REMOTE_FETCH, $response[PAYLOAD_STATE])); static::$journalRecord = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertTrue(is_array(static::$journalRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_GENERIC_FAIL . 'failed to fetch journal record'); } /** * testUpdateJournalRecovery() -- unit test method * * This unit test will invoke the audit/journal-recovery event to restore the previously-updated record back to * it's original state. In the previous update, we changed the string to "jayne" - this test will invoke the audit * restore event and then validate that the update was reversed. * * Assertions: * 1-6: static data persists * 7: Audit-restore event completed successfully (via the broker event request) * 8: That we were able to fetch the updated record * 9: That the test-string in the original record matches the updated record * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 01-07-19 mks DB-79: original coding * */ public function testUpdateJournalRecovery() { // validate that the carry-over data exists $this->assertTrue(validateGUID(static::$eventRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$eventRecordToken); $this->assertTrue(validateGUID(static::$testRecordToken), (basename(__FILE__) . COLON . __LINE__) . ERROR_INVALID_GUID . static::$testRecordToken); $this->assertTrue(is_array(static::$originalTestRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'originalTestRecord'); $this->assertTrue(validateGUID(static::$auditRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(is_array(static::$auditRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'auditRecord'); $this->assertTrue(is_array(static::$journalRecord), (basename(__FILE__) . COLON . __LINE__) . ERROR_UT_LOST_VARIABLE . 'journalRecord'); // build the recovery query to restore the original record $query = [ STRING_KEY => static::$auditRecordToken ]; $meta = static::$adminMeta; $meta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $request = [ BROKER_REQUEST => BROKER_REQUEST_AUDIT_RESTORE, BROKER_DATA => $query, BROKER_META_DATA => $meta ]; $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_AUDIT_RESTORE, $response[PAYLOAD_STATE])); // if we're still executing, that the record was "restored" --- attempt to pull the record $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [static::$testRecordToken ]]]]; $meta = static::$meta; $meta[META_DONUT_FILTER] = 1; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $meta ]; $payload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertEquals(STATE_SUCCESS, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . sprintf(ERROR_UT_VALS_NOT_EQUAL, STATE_SUCCESS, $response[PAYLOAD_STATE])); // compare the string field to the original record - they should be the same, or at least not jayne $originalString = static::$originalTestRecord[TEST_FIELD_TEST_STRING . COLLECTION_MONGO_TEST_EXT]; $updatedString = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0][TEST_FIELD_TEST_STRING . COLLECTION_MONGO_TEST_EXT]; $this->assertEquals($originalString, $updatedString, (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_VALS_NOT_EQUAL, $originalString, $updatedString)); } /** * testUpdateMany() -- unit test method * * this unit test is similar to the previous test except for: * * -- we're executing a more-complex query which may not return records to update * -- we had to provide query-options so that namaste would update past the first matching record * * 1. assert that the widget object persisted * -- generate a target value for matching the test-integer field * -- generate the query * -- submit query to the broker and process the return payload * 2. payload return a success status * 3. payload return records (if null, it could mean that the query did not pick up qualifying records) * -- grab random updated record from cache * 4. verify that the string was updated in the refreshed cache record * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 08-01-17 mks CORE-486: original coding * 03-28-19 mks DB-116: refactored selection query to limit the update to affecting a max of 10 records * 06-18-19 mks DB-122: support for META_LIMIT and mongoDB update scope * */ public function testUpdateMany() { $recordCount = 5; $testRecord = null; $this->assertTrue(is_object(static::$widget), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_WIDGET_404); // plan is to update the records that have an active status $query = [ CM_TST_FIELD_TEST_STATUS => [ OPERAND_NULL => [ OPERATOR_EQ => [ STATUS_ACTIVE ]]]]; // we're going to update the string field to this: $jayne = "Ten percent of nothin' is ... let me do the math here ... nothin' into nothin' ... carry the nothin' ... "; // build the update query $update = [ CM_TST_FIELD_TEST_STRING => $jayne ]; // build the broker-event payload $payload = [ STRING_QUERY_DATA => $query, STRING_UPDATE_DATA => $update ]; $meta = static::$meta; $meta[META_LIMIT] = $recordCount; // namaste will limit the delete to $recordCount records // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_UPDATE, BROKER_DATA => $payload, BROKER_META_DATA => $meta ]; // submit the request and unpack the results $eventPayload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); // verify that the update was successful // json_decode(gzuncompress(gasCache::get('')), true); $this->assertTrue($response[PAYLOAD_STATUS], (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_EXPECTING_TRUE . PAYLOAD_STATUS); $this->assertNotNull($response[PAYLOAD_RESULTS], (__FILE__ . COLON . __LINE__) . ERROR_UT_EMPTY_RESULTS); // grab the cache-key and fetch the data $cData = json_decode(gzuncompress(gasCache::get($response[PAYLOAD_RESULTS][0])), true); unset($cData[STRING_CHECKSUM]); $this->assertEquals(count($cData), $recordCount, basename(__METHOD__) . AT . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, count($cData), $recordCount)); $rr = mt_rand(0, $recordCount); // rr = random record $this->assertEquals($jayne, $cData[$rr][CM_TST_FIELD_TEST_STRING], basename(__METHOD__) . AT . __LINE__ . COLON . sprintf(ERROR_UT_STRING_MISMATCH, 'jayne', $cData[$rr][CM_TST_FIELD_TEST_STRING])); } /** * testNegativeUpdateOne() -- unit test method * * This unit test tests the ability to update a record by resetting a field marked as protected. If the framework * processes the request correctly, the event should fail with a validation state error. * * Create a request to update a record -- the field to be updated is protected -- and test the return payload * for a false status and a validation error in the state return. * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 09-11-17 mks CORE-558: original coding * */ public function testNegativeUpdateOne() { $testRecord = null; $this->assertTrue(is_object(static::$widget), (basename(__METHOD__) . AT . __LINE__ . COLON) . ERROR_UT_WIDGET_404); // we lost the cached record in insertJournalRecovery() - so fetch a new record $meta = static::$meta; $meta[META_LIMIT] = 1; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => [] ], BROKER_META_DATA => $meta ]; // submit the request and unpack the results $eventPayload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); static::$cKey = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; // get the cached record and extract the guid and the text string (query/replace values) $testRecord = gasCache::get(static::$cKey); $this->assertNotNull($testRecord, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL); $testRecord = json_decode(gzuncompress($testRecord), true); $testRecord = $testRecord[0]; $guid = $testRecord[CM_TST_TOKEN]; // this is the raw guid $this->assertTrue(validateGUID($guid), (basename(__METHOD__) . AT . __LINE__ . COLON) . ERROR_INVALID_GUID . $guid); $this->assertNotEmpty($guid, (basename(__METHOD__) . AT . __LINE__ . COLON) . ERROR_UT_FIELD_404 . CM_TST_TOKEN); // build a simple update query $query = [ CM_TST_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [$guid] ] ] ]; // build the update query using a protected field -- doesn't matter since it should be rejected/dropped $update = [ CM_TST_FIELD_TEST_CDATE => STRING_DATA ]; // meta data should reflect a client request as unit-test, as a client, will bypass the protected field check $meta = static::$meta; $meta[META_CLIENT] = CLIENT_CLIENT; // build the broker-update-event payload $payload = [ STRING_QUERY_DATA => $query, STRING_UPDATE_DATA => $update ]; // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_UPDATE, BROKER_DATA => $payload, BROKER_META_DATA => static::$meta ]; // submit the request and process the result payload $eventPayload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); $this->assertFalse($response[PAYLOAD_STATUS], (basename(__METHOD__) . AT . __LINE__ . COLON) . ERROR_UT_EXPECTING_FALSE . BROKER_REQUEST_UPDATE); $this->assertEquals(STATE_VALIDATION_ERROR, $response[PAYLOAD_STATE], (basename(__METHOD__) . AT . __LINE__ . COLON) . sprintf(ERROR_UT_STRING_MISMATCH, STATE_VALIDATION_ERROR, $response[PAYLOAD_STATE])); } /** * testDeleteOneRecord() -- unit test method * * this method tests the deletion of a single record, denoted by the cache-key we created earlier and stored * as a member property. * * 1. validate that the widget is still instantiated * 2. validate that the cacheKey still exists * -- Fetch the record from cache and extract the guid. * 3. validate the cache-extracted guid * 4. validate the cached record has status = active * -- build the query, submit, and unpack the return payload * 5. validate the response returned a status of true * -- fetch the record from cache * 6. validate that the record status is not long available in cache * * Since the test class has soft-deletes configured, we can pull the update from cache. * * Notes: * ------ * Created JIRA case: CORE-497 to delete cached records * * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 08-01-17 mks CORE-486: original coding * 08-07-17 mks CORE-497: update for the removal of deleted records from cache * 01-08-19 mks DB-80: saving original record data, and the delete event guid, to static members * for audit/journal testing later * */ public function testDeleteOneRecord() { $testRecord = null; // we've deleted the test record so, fetch any record with an active status static::$meta[META_LIMIT] = 1; $query = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => [] ], BROKER_META_DATA => static::$meta ]; $payload = gzcompress(json_encode($query)); $response = $this->readBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_FETCH, $response[PAYLOAD_STATE])); static::$cKey = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0]; // fetch the cached record so we can get the guid $record = gasCache::get(static::$cKey); $this->assertNotNull($record, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL); $record = json_decode(gzuncompress($record), true); $record = $record[0]; $guid = $record[CM_TST_TOKEN]; // validate the guid $this->assertTrue(validateGUID($guid), (__FILE__ . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . $guid); // save the original record for testing audit/journaling in following test methods static::$testRecordToken = $guid; static::$originalTestRecord = $record; // validate that the current record has an ACTIVE (e.g.: not-deleted) status $this->assertEquals(STATUS_ACTIVE, $record[CM_TST_FIELD_TEST_STATUS], (__FILE__ . COLON . __LINE__) . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_STATUS)); // delete the record we created a couple methods ago referenced by the static $cKey value $query = [ CM_TST_TOKEN => [ OPERAND_NULL => [OPERATOR_EQ => [$guid]]]]; $request = [ STRING_QUERY_DATA => $query]; // save the event guid for audit/journal record look-ups later $eventGUID = guid(); $this->assertTrue(validateGUID($eventGUID), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . $eventGUID); $meta = static::$meta; $meta[META_EVENT_GUID] = $eventGUID; static::$eventRecordToken = $eventGUID; // create the query/broker-event payload $payload = [ BROKER_REQUEST => BROKER_REQUEST_DELETE, BROKER_DATA => $request, BROKER_META_DATA => $meta ]; // submit the request and unpack the results $eventPayload = gzcompress(json_encode($payload)); $response = $this->writeBrokerClient->call($eventPayload); $response = json_decode(gzuncompress($response), true); // verify that the update was successful $this->assertTrue($response[PAYLOAD_STATUS], (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_EXPECTING_TRUE . PAYLOAD_STATUS); // fetch record from cache $record = gasCache::get(static::$cKey); $this->assertNull($record, (__FILE__ . COLON . __LINE__) . COLON . ERROR_UT_CACHE_FETCH_FAIL . COLON . $guid); } /** * testDeleteAudit() -- unit test method * * This unit test confirms that the record, deleted in the previous test-method, was successfully deleted and that * an audit record was created for that event. * * Assertions: * ----------- * 1-3: saved variables from the previous method are accessible and valid * 4: We successfully fetched the audit record from admin service (implying that the audit token was created) * 5: We pull a valid audit-record token from the data payload * 6: We pull the audit record and store it (confirming array format) * 7: We successfully fetch the original record * 8: That the fetch event resulted in a status of SUCCESS (soft delete) or NOT_FOUND (hard delete) * 9: If the delete was soft, that the status of the record was changed to DELETED * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 01-09-19 mks DB-80: original coding * */ public function testDeleteAudit() { sleep(1); // ensure we've completed writing the audit(delete) event record $ext = '_aud'; // ensure we've got the necessary saved data $this->assertTrue(validateGUID(static::$eventRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'eventGUID'); $this->assertTrue(validateGUID(static::$testRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'testRecordToken'); $this->assertTrue(is_array(static::$originalTestRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'originalTestRecord'); // validate that we have an audit record for the delete operation that just occurred $adminMeta = static::$adminMeta; $adminMeta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $adminMeta[META_DONUT_FILTER] = 1; $query = [ AUDIT_RECORD_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$testRecordToken ]]], DB_EVENT_GUID => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$eventRecordToken ]]], OPERAND_AND => null ]; $request = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $adminMeta ]; $response = $this->adminOutClient->call(gzcompress(json_encode($request))); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_REMOTE_FETCH, $response[PAYLOAD_STATE])); // grab and save the audit record token, and the audit record, for the next method... static::$auditRecordToken = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0][(DB_TOKEN . $ext)]; static::$auditRecord = $response[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0]; $this->assertNotNull(static::$auditRecord, basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_UT_NULL_VALUE . STRING_GUID_KEY); $this->assertTrue(validateGUID(static::$auditRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(is_array(static::$auditRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_FIELD_VALUE, 'auditRecord')); // pull the original record and let's confirm that it's deleted or in a deleted state $meta = static::$meta; $meta[META_DONUT_FILTER] = 1; $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$testRecordToken]]], DB_STATUS => [ OPERAND_NULL => [ OPERATOR_EQ => [ STATUS_DELETED ]]], OPERAND_AND => null ]; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $meta ]; $response = $this->readBrokerClient->call(gzcompress(json_encode($request))); $results = json_decode(gzuncompress($response), true); $this->assertTrue($results[PAYLOAD_STATUS], (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_FETCH, $results[PAYLOAD_STATE])); $state = $results[PAYLOAD_STATE]; $vString = STATE_SUCCESS . ' or ' . STATE_NOT_FOUND; $this->assertTrue(($state == STATE_SUCCESS or $state == STATE_NOT_FOUND), (basename(__FILE__) . COLON . __LINE__) . sprintf(ERROR_UT_VALS_NOT_EQUAL, $state, $vString)); if ($state == STATE_SUCCESS) { $newStatus = $results[PAYLOAD_RESULTS][STRING_QUERY_RESULTS][0][(DB_STATUS . COLLECTION_MONGO_TEST_EXT)]; $this->assertEquals(STATUS_DELETED, $newStatus, (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_VALS_NOT_EQUAL, STATUS_DELETED, $newStatus)); } } /** * testDeleteJournalRecovery() -- unit test method * * This method tests that a journal record was created off the audit event from the delete-record event posted * a couple of methods ago. We make the following assertions in this test: * * 1-5: That all stored variables are in the correct format and available through class statics * 6: That we're able to fetch a journal record (implicitly stating that the journal record was created) * 7: That we're able to successfully post a audit-recovery event and that the event successfully completes * 8: That deleted record has been recovered back to an active state * * * @author mike@givingassistant.org * @version 1.0 * * * HISTORY: * ======== * 01-09-19 mks DB-80: original coding * */ public function testDeleteJournalRecovery() { $this->assertTrue(validateGUID(static::$eventRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'eventGUID'); $this->assertTrue(validateGUID(static::$testRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'testRecordToken'); $this->assertTrue(is_array(static::$originalTestRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_UT_LOST_VARIABLE . 'originalTestRecord'); $this->assertTrue(validateGUID(static::$auditRecordToken), (basename(__FILE__) . COLON . __LINE__) . COLON . ERROR_INVALID_GUID . static::$auditRecordToken); $this->assertTrue(is_array(static::$auditRecord), (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_FIELD_VALUE, 'auditRecord')); // build the request to fetch the journal record for the delete event $meta = static::$adminMeta; $meta[META_DONUT_FILTER] = 1; $meta[META_TEMPLATE] = TEMPLATE_CLASS_JOURNAL; $query = [ JOURNAL_AUD_TOK => [ OPERAND_NULL => [ OPERATOR_EQ => [ static::$auditRecordToken ]]]]; $request = [ BROKER_REQUEST => BROKER_REQUEST_REMOTE_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $meta ]; $response = $this->adminOutClient->call(gzcompress(json_encode($request))); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], (basename(__FILE__) . COLON . __LINE__) . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_REMOTE_FETCH, $response[PAYLOAD_STATE])); // now that we've confirmed that the journal record exists, invoke the recovery event to restore the // original record back to it's non-deleted state $query = [ STRING_KEY => static::$auditRecordToken ]; $meta = static::$adminMeta; $meta[META_TEMPLATE] = TEMPLATE_CLASS_AUDIT; $request = [ BROKER_REQUEST => BROKER_REQUEST_AUDIT_RESTORE, BROKER_DATA => $query, BROKER_META_DATA => $meta ]; $payload = gzcompress(json_encode($request)); $response = $this->adminOutClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_AUDIT_RESTORE, $response[PAYLOAD_STATE])); // if we're still executing, that the record was "restored" --- attempt to pull the record after a // slightly-respectful delay to give time for the update to propagate... sleep(1); $query = [ DB_TOKEN => [ OPERAND_NULL => [ OPERATOR_EQ => [static::$testRecordToken ]]]]; $meta = static::$meta; $meta[META_DONUT_FILTER] = 1; $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [ STRING_QUERY_DATA => $query ], BROKER_META_DATA => $meta ]; $payload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); $this->assertEquals(STATE_SUCCESS, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . sprintf(ERROR_UT_VALS_NOT_EQUAL, STATE_SUCCESS, $response[PAYLOAD_STATE])); } /** * grabRandomTestRecord() -- private method for code reduction * * this is a private function that fetches a random record from the test collection. The function is invoked in * the two sub-collection methods immediately following this one. * * There is an optional input parameter to this method: * * $_limit -- integer value, that defaults to 1, allowing you to specify the number of records to be returned, * up to the maximum allowable. * * the function build a read-broker query, submits it, unpacks the result, and then validates/tests the return * status. If the request was successful, we'll return the response payload otherwise execution of the testing * will halt. * * @author mike@givingassistant.org * @version 1.0 * * @param int $_limit -- optional parameter allowing user to specify number of records to be returned * @return array * * HISTORY: * ======== * 09-07-17 mks CORE-494: original coding * 03-28-19 mks DB-116: added $_limit optional parameter * */ private function grabRandomTestRecord(int $_limit = 1): array { $this->assertTrue($this->readBrokerClient->status, __METHOD__ . __LINE__ . ERROR_BROKER_QUEUE_DECLARE . BROKER_QUEUE_R); $this->assertTrue($this->writeBrokerClient->status, __METHOD__ . __LINE__ . ERROR_BROKER_QUEUE_DECLARE . BROKER_QUEUE_W); // validate the amount passed, if any, via the $_limit parameter $_limit = (!is_numeric($_limit)) ? 1 : intval($_limit); if ($_limit > gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_QUERY_RECORD_LIMIT]) $_limit = gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_QUERY_RECORD_LIMIT]; // first thing we want to do is pull a random record in active status... $query = [ CM_TST_FIELD_TEST_STATUS => [ OPERAND_NULL => [ OPERATOR_EQ => [ STATUS_ACTIVE ]]], CM_TST_FIELD_TEST_INT => [ OPERAND_NULL => [ OPERATOR_EQ => [ intval(mt_rand(0,20))]]], OPERAND_AND => null ]; // limit the return data count to 1 record (we'll test for this...) static::$meta[META_LIMIT] = $_limit; // set up the broker request $request = [ BROKER_REQUEST => BROKER_REQUEST_FETCH, BROKER_DATA => [STRING_QUERY_DATA => $query ], BROKER_META_DATA => static::$meta ]; // build the request payload and submit the request to the read broker $payload = gzcompress(json_encode($request)); $response = $this->readBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); unset(static::$meta[META_LIMIT]); $this->assertTrue($response[PAYLOAD_STATUS], (__FILE__ . COLON) . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_FETCH, $response[PAYLOAD_STATE])); return($response); } /** * testSubCollectionInserts() -- unit test method * * This method evaluates the wBroker sub-Collection insert event by adding a new subC record to a randomly * selected test record. * * NOTE THAT THIS TEST WILL ONLY WORK ON A CACHE-SUPPORTED CLASS! * * Criteria for a successful test: * ------------------------------- * 1. The readBroker class object is still available * 2. The writeBroker class object is still available * 3. We can fetch a random, active, record * 4. That we can fetch the same record from cache * 5. That we can successfully publish a subC insert event * 6. That we can fetch the updated (reflecting the db status) record from cache * 7. That the subC count incremented by 1 record * 8. That the inserted record exists * 9. That all of the inserted record fields were set correctly * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 08-29-17 mks CORE-494: initial coding * 09-06-17 mks CORE-556: injecting guids into sub-collection records on create * 09-07-17 mks CORE-494: broke fetch-random record out to private method and invoked * */ public function testSubCollectionInserts() { $response = $this->grabRandomTestRecord(); sleep(1); $ck = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertNotNull($ck,basename(__METHOD__) . AT . __LINE__ . COLON . ERROR_UT_LOST_VARIABLE . STRING_CACHE_CKEY); $this->assertTrue(gasCache::validateCacheKey($ck), (__FILE__ . COLON) . __LINE__ . ERROR_INVALID_GUID . $ck); // we have the cache key for the record - fetch the record data $data = gasCache::get($ck); $data = json_decode(gzuncompress($data), true); $data = $data[0]; $this->assertTrue(is_array($data), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_CACHE_FETCH_FAIL); if (isset($data[CM_TST_FIELD_TEST_SUBC]) and count($data[CM_TST_FIELD_TEST_SUBC])) { $numRecsOld = count($data[CM_TST_FIELD_TEST_SUBC]); $this->assertGreaterThan(0, $numRecsOld, __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 0, $numRecsOld, 'number of subC recs')); } else { $numRecsOld = 0; } $key = $data[CM_TST_TOKEN]; $this->assertTrue(validateGUID($key), __FILE__ . COLON . __LINE__ . ERROR_INVALID_GUID . $key); // create and add a new subC record $newGUID = guid(); $intVal = 33; $doubleVal = 33.33; $boolVal = false; $newSubCRecord = [ [ CM_TST_FIELD_TEST_INT => $intVal, CM_TST_FIELD_TEST_DOUBLE => $doubleVal, CM_TST_FIELD_TEST_STRING => $newGUID, CM_TST_FIELD_TEST_NIF => STRING_UT_RECORD_INSERT, CM_TST_FIELD_TEST_BOOL => $boolVal ] ]; $field = CM_TST_FIELD_TEST_SUBC; $brokerData = [ STRING_GUID_KEY => $key, STRING_SUBC_FIELD => $field, STRING_DATA => $newSubCRecord ]; // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_SUBC_CREATE, BROKER_DATA => $brokerData, BROKER_META_DATA => static::$meta ]; $payload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); sleep(1); // give up some time for the cache record to be updated $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_SUBC_CREATE, $response[PAYLOAD_STATE])); $newCk = $response[PAYLOAD_RESULTS][0]; $this->assertTrue(gasCache::validateCacheKey($newCk), __FILE__ . AT . __LINE__ . COLON . ERROR_INVALID_GUID . $newCk); // using the "new key", fetch the record from cache and check the count of the sub-collection $newData = gasCache::get($newCk); $newData = json_decode(gzuncompress($newData), true); $this->assertTrue(is_array($newData), __FILE__ . AT . __LINE__ . COLON . ERROR_UT_CACHE_FETCH_FAIL); $newData = $newData[0]; $numRecsNew = count($newData[CM_TST_FIELD_TEST_SUBC]); $this->assertEquals(($numRecsOld + 1), $numRecsNew, __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, $numRecsNew, ($numRecsOld + 1), CM_TST_FIELD_TEST_SUBC)); // get new subC record and validate the added fields and the injected guid $subCRecord = $newData[CM_TST_FIELD_TEST_SUBC][$numRecsOld]; $this->assertEquals($subCRecord[CM_TST_FIELD_TEST_INT], $intVal, __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_INT)); $this->assertEquals($subCRecord[CM_TST_FIELD_TEST_DOUBLE], $doubleVal, __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_DOUBLE)); $this->assertEquals($subCRecord[CM_TST_FIELD_TEST_STRING], $newGUID, __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_STRING)); $this->assertEquals($subCRecord[CM_TST_FIELD_TEST_BOOL], $boolVal, __FILE__ . AT . __LINE__ . COLON . sprintf(ERROR_UT_SAME_FIELD_COMPARE_FAIL, CM_TST_FIELD_TEST_BOOL)); $this->assertNotEmpty($subCRecord[CM_TST_TOKEN], __FILE__ . COLON . __LINE__ . AT . ERROR_DATA_KEY_404 . CM_TST_TOKEN); $this->assertTrue(validateGUID($subCRecord[CM_TST_TOKEN]), __FILE__ . AT . __LINE__ . COLON . ERROR_INVALID_GUID . $subCRecord[CM_TST_TOKEN]); } /** * testNegativeSubCollectionInsert() -- unit test method * * This is a negative unit test in that we're intentionally submitting an event request we expect to fail. For this * test, we're looking at exceeding the maximum number of records allowed for a transaction. * * We'll pull a random record to get the guid values for the request - and then we'll also pull the max number * of allowing records-per-transaction value from the XML configuration file. * * Next, we'll build a data payload which exceeds the count of allowable records and then submit the event request. * * Our tests will validate that the request was rejected with the appropriate error state and status. * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 09-11-17 mks CORE-501: original coding * */ public function testNegativeSubCollectionInsert() { $records = null; $response = $this->grabRandomTestRecord(); $ck = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertTrue(gasCache::validateCacheKey($ck), (__FILE__ . COLON) . __LINE__ . ERROR_INVALID_GUID . $ck); // we have the cache key for the record - fetch the record data $data = gasCache::get($ck); $data = json_decode(gzuncompress($data), true); $data = $data[0]; $this->assertTrue(is_array($data), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_CACHE_FETCH_FAIL); $key = $data[CM_TST_TOKEN]; $this->assertTrue(validateGUID($key), __FILE__ . COLON . __LINE__ . ERROR_INVALID_GUID . $key); $qrl = intval(gasConfig::$settings[CONFIG_DATABASE][CONFIG_DATABASE_QUERY_RECORD_LIMIT]); $recCount = 1 + $qrl; // create and add a new subC record $newGUID = guid(); $intVal = 33; $doubleVal = 33.33; $boolVal = false; $newSubCRecord = [ [ CM_TST_FIELD_TEST_INT => $intVal, CM_TST_FIELD_TEST_DOUBLE => $doubleVal, CM_TST_FIELD_TEST_STRING => $newGUID, CM_TST_FIELD_TEST_NIF => STRING_UT_RECORD_INSERT, CM_TST_FIELD_TEST_BOOL => $boolVal ] ]; for ($index = 0; $index < $recCount; $index++) $records[] = $newSubCRecord; $field = CM_TST_FIELD_TEST_SUBC; $brokerData = [ STRING_GUID_KEY => $key, STRING_SUBC_FIELD => $field, STRING_DATA => $records ]; // create the query/broker-event payload $request = [ BROKER_REQUEST => BROKER_REQUEST_SUBC_CREATE, BROKER_DATA => $brokerData, BROKER_META_DATA => static::$meta ]; $payload = gzcompress(json_encode($request)); $response = $this->writeBrokerClient->call($payload); $response = json_decode(gzuncompress($response), true); // test that the event was rejected with the expected state value $this->assertFalse($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_EXPECTING_FALSE . BROKER_REQUEST_CREATE); $this->assertEquals(STATE_DATA_ERROR, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_STRING_MISMATCH, STATE_DATA_ERROR, $response[PAYLOAD_STATE])); } /** * testSubCollectionDeletes() -- unit test method * * this unit test evaluates the BROKER_REQUEST_SUBC_DELETE event in the write-broker by fetching a random record * from the test collection and selecting a sub-collection record (with a key) for deletion. * * The payload request is build and submitted to namaste for processing after which we evaluate the success or * fail for the event. * * next, because this is a cached-class, we're going to pull the updated record from cache using the reference to * the cache key returned in the broker-payload and we're going to test that the count of sub-collection records * was de-incremented by one and then we're going to spin through every sub-collection record with a key and compare * the key value. * * Finally, we're going to re-submit the request again to test that we got a successful event, but we returned a * 404 (STATE_NOT_FOUND) message. * * @author mike@givingassistant.org * @version 1.0 * * HISTORY: * ======== * 09-07-17 mks CORE-494: original coding * */ public function testSubCollectionDeletes() { $subCKey = ''; $found = false; $data = null; while (!$found) { $response = $this->grabRandomTestRecord(); $ck = $response[PAYLOAD_RESULTS][PAYLOAD_QUERY][0]; $this->assertTrue(gasCache::validateCacheKey($ck), (__FILE__ . COLON) . __LINE__ . ERROR_INVALID_GUID . $ck); // we have the cache key for the record - fetch the record data $data = gasCache::get($ck); $data = json_decode(gzuncompress($data), true); $data = $data[0]; $this->assertTrue(is_array($data), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_CACHE_FETCH_FAIL); $found = (isset($data[CM_TST_FIELD_TEST_SUBC]) and !empty($data[CM_TST_FIELD_TEST_SUBC])) ? true : false; } $numRecsOld = count($data[CM_TST_FIELD_TEST_SUBC]); $this->assertGreaterThan(0, $numRecsOld, __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_INTEGER_MISMATCH, 0, $numRecsOld, 'number of subC recs')); $key = $data[CM_TST_TOKEN]; $this->assertTrue(validateGUID($key), __FILE__ . COLON . __LINE__ . ERROR_INVALID_GUID . $key); $subCArray = $data[CM_TST_FIELD_TEST_SUBC]; $found = false; $index = 0; while (!$found and $index < $numRecsOld) { if (array_key_exists(CM_TST_TOKEN, $subCArray[$index])) { $found = true; $subCKey = $subCArray[$index][CM_TST_TOKEN]; } else { $index++; } } $this->assertTrue($found, __FILE__ . COLON . __LINE__ . COLON . ERROR_SUBC_RECORD_NO_KEY); // build and submit the request to delete the selected sub-collection record $payload = [ BROKER_REQUEST => BROKER_REQUEST_SUBC_DELETE, BROKER_DATA => [ STRING_GUID_KEY => $key, STRING_SUBC_FIELD => CM_TST_FIELD_TEST_SUBC, STRING_SUBC_GUID => $subCKey ], BROKER_META_DATA => static::$meta ]; $request = gzcompress(json_encode($payload)); $response = $this->writeBrokerClient->call($request); $response = json_decode(gzuncompress($response), true); // check that we successfully removed the sub-collection record // first, check that the request was successfully processed $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_SUBC_DELETE, $response[PAYLOAD_STATE])); $this->assertEquals(STATE_SUCCESS, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_SUBC_DELETE, $response[PAYLOAD_STATE])); // fetch the updated record from cache $newData = gasCache::get($response[PAYLOAD_RESULTS][0]); $newData = json_decode(gzuncompress($newData), true); $newData = $newData[0]; $this->assertTrue(is_array($newData), __FILE__ . COLON . __LINE__ . COLON . ERROR_UT_CACHE_FETCH_FAIL); $numRecsNew = !isset($newData[CM_TST_FIELD_TEST_SUBC]) ? 0 : count($newData[CM_TST_FIELD_TEST_SUBC]); if ($numRecsNew != 0) { $this->assertEquals(($numRecsOld - 1), $numRecsNew, __FILE__ . COLON . __LINE__ . sprintf(ERROR_UT_INTEGER_MISMATCH, $numRecsNew, ($numRecsOld + 1), CM_TST_FIELD_TEST_SUBC)); // search every sub-collection record with a key and compare it to the key in the delete request foreach ($newData[CM_TST_FIELD_TEST_SUBC] as $subRecord) { if (array_key_exists(CM_TST_TOKEN, $subRecord)) { $this->assertNotEquals($subRecord[CM_TST_TOKEN], $subCKey, __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_SUBC_RECORD_KEY_FOUND, $subCKey)); } } } // test the 404 return payload by re-submitting the request $request = gzcompress(json_encode($payload)); $response = $this->writeBrokerClient->call($request); $response = json_decode(gzuncompress($response), true); $this->assertTrue($response[PAYLOAD_STATUS], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_SUBC_DELETE, $response[PAYLOAD_STATE])); $this->assertEquals(STATE_NOT_FOUND, $response[PAYLOAD_STATE], __FILE__ . COLON . __LINE__ . COLON . sprintf(ERROR_UT_BROKER_EVENT_FAIL, BROKER_REQUEST_SUBC_DELETE, $response[PAYLOAD_STATE])); } }