Skip to content

Commit

Permalink
Merge pull request #97 from Countly/tests-and-refactoring
Browse files Browse the repository at this point in the history
Storage Tests
  • Loading branch information
turtledreams authored Aug 22, 2024
2 parents f57f741 + 8cf689c commit c205763
Show file tree
Hide file tree
Showing 4 changed files with 315 additions and 8 deletions.
45 changes: 37 additions & 8 deletions lib/countly-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,52 @@ var storagePath;
var __data = {};
var defaultPath = "../data/"; // Default path
var defaultBulkPath = "../bulk_data/"; // Default path
var asyncWriteLock = false;
var asyncWriteQueue = [];

/**
* Sets storage path
* If user didn't provide a path, it sets to default path "../data/"
* Instead if a path is provided, sets the storage path as the provided path
* @param {String} userPath - user provided storage path
*/
var setStoragePath = function(userPath) {
storagePath = userPath || defaultPath;
var dir = path.resolve(__dirname, getStoragePath());
if (userPath === undefined || userPath === null) {
storagePath = defaultPath;
}
else {
storagePath = userPath;
}
var dir = path.resolve(__dirname, storagePath);
createDirectory(dir);
};

/**
* Sets bulk storage path if persistQueue is enabled.
* If user didn't provide a path, it sets to default path "../bulk_data/"
* Instead if a path is provided, sets the storage path as the provided path
* @param {String} userPath - user provided storage path
* @param {Boolean} persistQueue - whether to persistently store queue until processed. false in default
*/
var setBulkDataPath = function(userPath, persistQueue) {
storagePath = userPath || defaultBulkPath;
if (userPath === undefined || userPath === null) {
storagePath = defaultBulkPath;
}
else {
storagePath = userPath;
}
var dir = path.resolve(__dirname, getStoragePath());
if (persistQueue) {
createDirectory(dir);
}
};

/**
* Returns the storage path
* @returns {String} storage path
*/
var getStoragePath = function() {
return storagePath;
return storagePath || undefined;
};

var createDirectory = function(dir) {
Expand Down Expand Up @@ -94,9 +123,6 @@ var forceStore = function() {
}
};

var asyncWriteLock = false;
var asyncWriteQueue = [];

/**
* Write to file and process queue while in asyncWriteLock
* @param {String} key - key for value to store
Expand All @@ -117,7 +143,10 @@ var writeFile = function(key, value, callback) {
if (asyncWriteQueue.length) {
setTimeout(() => {
var arr = asyncWriteQueue.shift();
writeFile(arr[0], arr[1], arr[2]);
cc.log(cc.logLevelEnums.DEBUG, "writeFile, Dequeued array:", arr);
if (arr) {
writeFile(arr[0], arr[1], arr[2]);
}
}, 0);
}
else {
Expand Down
3 changes: 3 additions & 0 deletions lib/countly.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ Countly.Bulk = Bulk;

// Common module debug value is set to init time debug value
cc.debug = conf.debug;

// Set the storage path
CountlyStorage.setStoragePath(conf.storage_path);

// clear stored device ID if flag is set
Expand Down Expand Up @@ -717,6 +719,7 @@ Countly.Bulk = Bulk;
add_cly_events(event);
}
};

/**
* Add events to event queue
* @memberof Countly._internals
Expand Down
4 changes: 4 additions & 0 deletions test/customStorageDirectory/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore
271 changes: 271 additions & 0 deletions test/tests_storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
const assert = require("assert");
var Countly = require("../lib/countly");
var storage = require("../lib/countly-storage");
var cc = require("../lib/countly-common");
var hp = require("./helpers/helper_functions");

// example event object to use
var eventObj = {
key: "storage_check",
count: 5,
sum: 3.14,
dur: 2000,
segmentation: {
app_version: "1.0",
country: "Zambia",
},
};

var userDetailObj = {
name: "Akira Kurosawa",
username: "a_kurosawa",
email: "[email protected]",
organization: "Toho Studios",
phone: "+81312345678",
picture: "https://example.com/profile_images/akira_kurosawa.jpg",
gender: "Male",
byear: 1910,
custom: {
"known for": "Film Director",
"notable works": "Seven Samurai, Rashomon, Ran",
},
};

// init function
function initMain(device_id) {
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
interval: 10000,
max_events: -1,
device_id: device_id,
});
}
// TODO: move these to helpers to reduce duplication
function validateSdkGeneratedId(providedDeviceId) {
assert.ok(providedDeviceId);
assert.equal(providedDeviceId.length, 36);
assert.ok(cc.isUUID(providedDeviceId));
}
function checkRequestsForT(queue, expectedInternalType) {
for (var i = 0; i < queue.length; i++) {
assert.ok(queue[i].t);
assert.equal(queue[i].t, expectedInternalType);
}
}
function validateDeviceId(deviceId, deviceIdType, expectedDeviceId, expectedDeviceIdType) {
var rq = hp.readRequestQueue()[0];
if (expectedDeviceIdType === cc.deviceIdTypeEnums.SDK_GENERATED) {
validateSdkGeneratedId(deviceId); // for SDK-generated IDs
}
else {
assert.equal(deviceId, expectedDeviceId); // for developer-supplied IDs
}
assert.equal(deviceIdType, expectedDeviceIdType);
checkRequestsForT(rq, expectedDeviceIdType);
}
function recordValuesToStorageAndValidate() {
// Set values
var deviceIdType = cc.deviceIdTypeEnums.DEVELOPER_SUPPLIED;
storage.storeSet("cly_id", "SpecialDeviceId");
storage.storeSet("cly_id_type", deviceIdType);

// Set values with different data types
storage.storeSet("cly_count", 42);
storage.storeSet("cly_object", { key: "value" });
storage.storeSet("cly_null", null);

// Retrieve and assert values
assert.equal(storage.storeGet("cly_id"), "SpecialDeviceId");
assert.equal(storage.storeGet("cly_id_type"), deviceIdType);
assert.equal(storage.storeGet("cly_count"), 42);
assert.deepEqual(storage.storeGet("cly_object"), { key: "value" });
assert.equal(storage.storeGet("cly_null"), null);

// Remove specific items by overriding with null or empty array
storage.storeSet("cly_id", null);
storage.storeSet("cly_object", []);
assert.equal(storage.storeGet("cly_id"), null);
assert.deepEqual(storage.storeGet("cly_object"), []);

// Reset storage and check if it's empty again
storage.resetStorage();
assert.equal(storage.storeGet("cly_id"), undefined);
assert.equal(storage.storeGet("cly_id_type"), undefined);
assert.equal(storage.storeGet("cly_count"), undefined);
assert.equal(storage.storeGet("cly_object"), undefined);
assert.equal(storage.storeGet("cly_null"), undefined);
}

describe("Storage Tests", () => {
it("1- Store Generated Device ID", (done) => {
// clear previous data
hp.clearStorage();
// initialize SDK
initMain();
Countly.begin_session();
// read request queue
setTimeout(() => {
validateSdkGeneratedId(Countly.get_device_id());
done();
}, hp.sWait);
});

it("1.1- Validate generated device id after process restart", (done) => {
initMain();
validateDeviceId(Countly.get_device_id(), Countly.get_device_id_type(), undefined, cc.deviceIdTypeEnums.SDK_GENERATED);
done();
});

it("2.Developer supplied device ID", (done) => {
hp.clearStorage();
initMain("ID");
Countly.begin_session();
setTimeout(() => {
validateDeviceId(Countly.get_device_id(), Countly.get_device_id_type(), "ID", cc.deviceIdTypeEnums.DEVELOPER_SUPPLIED);
done();
}, hp.sWait);
});

it("2.1- Validate generated device id after process restart", (done) => {
validateDeviceId(Countly.get_device_id(), Countly.get_device_id_type(), "ID", cc.deviceIdTypeEnums.DEVELOPER_SUPPLIED);
done();
});

it("3- Record and validate all user details", (done) => {
hp.clearStorage();
initMain();
Countly.user_details(userDetailObj);
setTimeout(() => {
var req = hp.readRequestQueue()[0];
hp.userDetailRequestValidator(userDetailObj, req);
done();
}, hp.sWait);
});

it("3.1- Validate stored user detail", (done) => {
var req = hp.readRequestQueue()[0];
hp.userDetailRequestValidator(userDetailObj, req);
done();
});

it("4- Record event and validate storage", (done) => {
hp.clearStorage();
initMain();
Countly.add_event(eventObj);
setTimeout(() => {
var storedEvents = hp.readEventQueue();
assert.strictEqual(storedEvents.length, 1, "There should be exactly one event stored");

var event = storedEvents[0];
hp.eventValidator(eventObj, event);
done();
}, hp.mWait);
});

it("4.1- Validate event persistence after process restart", (done) => {
// Initialize SDK
initMain();

// Read stored events without clearing storage
var storedEvents = hp.readEventQueue();
assert.strictEqual(storedEvents.length, 1, "There should be exactly one event stored");

var event = storedEvents[0];
hp.eventValidator(eventObj, event);
done();
});

// if storage path is not provided it will be default "../data/"
it("5- Not provide storage path during init", (done) => {
hp.clearStorage();
initMain();
assert.equal(storage.getStoragePath(), "../data/");
done();
});

// if set to undefined it should be set to default path
it("6- Set storage path to undefined", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
storage_path: undefined,
});
assert.equal(storage.getStoragePath(), "../data/");
done();
});

// if set to null it should be set to default path
it("7- Set storage path to null", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
storage_path: null,
});
assert.equal(storage.getStoragePath(), "../data/");
done();
});

// it should be set to the custom directory if provided
it("8- Set storage path to custom directory", (done) => {
hp.clearStorage();
Countly.init({
app_key: "YOUR_APP_KEY",
url: "https://test.url.ly",
interval: 10000,
max_events: -1,
storage_path: "../test/customStorageDirectory/",
});
assert.equal(storage.getStoragePath(), "../test/customStorageDirectory/");
done();
});

it("9- Reset Storage While on Default Path /no-init", (done) => {
// will set to default storage path
storage.setStoragePath();
assert.equal(storage.getStoragePath(), "../data/");
// will set to undefined
storage.resetStorage();
assert.equal(storage.getStoragePath(), undefined);
done();
});

it("10- Recording to Storage with Default Storage Path /no-init", (done) => {
storage.resetStorage();

// Set to default storage path
storage.setStoragePath();
assert.equal(storage.getStoragePath(), "../data/");
recordValuesToStorageAndValidate();
done();
});

it("11- Recording to Storage with Custom Storage Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
storage.setStoragePath("../test/customStorageDirectory/");
assert.equal(storage.getStoragePath(), "../test/customStorageDirectory/");
recordValuesToStorageAndValidate();
done();
});

it("12- Recording to Bulk Storage with Default Bulk Data Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
storage.setBulkDataPath();
assert.equal(storage.getStoragePath(), "../bulk_data/");
recordValuesToStorageAndValidate();
done();
});

it("13- Recording to Bulk Storage with Custom Bulk Storage Path /no-init", (done) => {
storage.resetStorage();
// will set to default storage path
storage.setBulkDataPath("../test/customStorageDirectory/");
assert.equal(storage.getStoragePath(), "../test/customStorageDirectory/");
recordValuesToStorageAndValidate();
done();
});
});

0 comments on commit c205763

Please sign in to comment.