Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage Tests #97

Merged
merged 5 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
});
});
Loading