unit test + some refactoring

This commit is contained in:
Henry Jameson 2022-08-04 22:09:42 +03:00
commit 8c59bad3c2
3 changed files with 277 additions and 86 deletions

View file

@ -1,13 +1,14 @@
import { toRaw } from 'vue'
import { isEqual } from 'lodash'
import { isEqual, cloneDeep } from 'lodash'
import { CURRENT_UPDATE_COUNTER } from 'src/components/update_notification/update_notification.js'
export const VERSION = 1
export const NEW_USER_DATE = new Date('04-08-2022') // date of writing this, basically
export const NEW_USER_DATE = new Date('2022-08-04') // date of writing this, basically
export const COMMAND_TRIM_FLAGS = 1000
export const COMMAND_TRIM_FLAGS_AND_RESET = 1001
const defaultState = {
export const defaultState = {
// do we need to update data on server?
dirty: false,
// storage of flags - stuff that can only be set and incremented
@ -27,9 +28,9 @@ const defaultState = {
cache: null
}
const newUserFlags = {
export const newUserFlags = {
...defaultState.flagStorage,
updateCounter: 1 // new users don't need to see update notification
updateCounter: CURRENT_UPDATE_COUNTER // new users don't need to see update notification
}
const _wrapData = (data) => ({
@ -38,24 +39,23 @@ const _wrapData = (data) => ({
_version: VERSION
})
export const _checkValidity = (data) => data._timestamp > 0 && data._version > 0
const _checkValidity = (data) => data._timestamp > 0 && data._version > 0
export const _getRecentData = (cache, live) => {
const result = { recent: null, stale: null, needUpload: false }
const cacheValid = _checkValidity(cache || {})
const liveValid = _checkValidity(live || {})
if (!liveValid) {
if (!liveValid && cacheValid) {
result.needUpload = true
console.debug('Nothing valid stored on server, assuming cache to be source of truth')
result.recent = cache
result.stale = live
} else if (!cacheValid) {
} else if (!cacheValid && liveValid) {
console.debug('Valid storage on server found, no local cache found, using live as source of truth')
result.recent = live
result.stale = cache
} else {
} else if (cacheValid && liveValid) {
console.debug('Both sources have valid data, figuring things out...')
console.log(live._timestamp, cache._timestamp)
if (live._timestamp === cache._timestamp && live._version === cache._version) {
console.debug('Same version/timestamp on both source, source of truth irrelevant')
result.recent = cache
@ -70,14 +70,17 @@ export const _getRecentData = (cache, live) => {
result.stale = cache
}
}
} else {
console.debug('Both sources are invalid, start from scratch')
result.needUpload = true
}
return result
}
export const _getAllFlags = (recent = {}, stale = {}) => {
export const _getAllFlags = (recent, stale) => {
return Array.from(new Set([
...Object.keys(toRaw(recent.flagStorage || {})),
...Object.keys(toRaw(stale.flagStorage || {}))
...Object.keys(toRaw((recent || {}).flagStorage || {})),
...Object.keys(toRaw((stale || {}).flagStorage || {}))
]))
}
@ -86,37 +89,43 @@ export const _mergeFlags = (recent, stale, allFlagKeys) => {
const recentFlag = recent.flagStorage[flag]
const staleFlag = stale.flagStorage[flag]
// use flag that is of higher value
return [flag, recentFlag > staleFlag ? recentFlag : staleFlag]
return [flag, Number((recentFlag > staleFlag ? recentFlag : staleFlag) || 0)]
}))
}
export const _resetFlags = (totalFlags, allFlagKeys) => {
export const _resetFlags = (totalFlags, knownKeys = defaultState.flagStorage) => {
let result = { ...totalFlags }
const allFlagKeys = Object.keys(totalFlags)
// flag reset functionality
if (totalFlags.reset >= COMMAND_TRIM_FLAGS && totalFlags.reset <= COMMAND_TRIM_FLAGS_AND_RESET) {
console.debug('Received command to trim the flags')
const knownKeys = new Set(Object.keys(defaultState.flagStorage))
const knownKeysSet = new Set(Object.keys(knownKeys))
// Trim
result = {}
allFlagKeys.forEach(flag => {
if (!knownKeys.has(flag)) {
delete totalFlags[flag]
if (knownKeysSet.has(flag)) {
result[flag] = totalFlags[flag]
}
})
// Reset
if (totalFlags.reset === COMMAND_TRIM_FLAGS_AND_RESET) {
// 1001 - and reset everything to 0
console.debug('Received command to reset the flags')
allFlagKeys.forEach(flag => { totalFlags[flag] = 0 })
} else {
// reset the reset 0
totalFlags.reset = 0
Object.keys(knownKeys).forEach(flag => { result[flag] = 0 })
}
} else if (totalFlags.reset > 0 && totalFlags.reset < 9000) {
console.debug('Received command to reset the flags')
allFlagKeys.forEach(flag => { totalFlags[flag] = 0 })
// for good luck
totalFlags.reset = 0
allFlagKeys.forEach(flag => { result[flag] = 0 })
}
result.reset = 0
return result
}
export const _doMigrations = (cache) => {
if (!cache) return cache
if (cache._version < VERSION) {
console.debug('Local cached data has older version, seeing if there any migrations that can be applied')
@ -139,65 +148,69 @@ export const _doMigrations = (cache) => {
return cache
}
export const mutations = {
setServerSideStorage (state, userData) {
const live = userData.storage
state.raw = live
let cache = state.cache
cache = _doMigrations(cache)
let { recent, stale, needsUpload } = _getRecentData(cache, live)
const userNew = userData.created_at > NEW_USER_DATE
const flagsTemplate = userNew ? newUserFlags : defaultState.flagStorage
let dirty = false
if (recent === null) {
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
recent = _wrapData({
flagStorage: { ...flagsTemplate }
})
}
if (!needsUpload && recent && stale) {
console.debug('Checking if data needs merging...')
// discarding timestamps and versions
const { _timestamp: _0, _version: _1, ...recentData } = recent
const { _timestamp: _2, _version: _3, ...staleData } = stale
dirty = !isEqual(recentData, staleData)
console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`)
}
const allFlagKeys = _getAllFlags(recent, stale)
let totalFlags
if (dirty) {
// Merge the flags
console.debug('Merging the flags...')
totalFlags = _mergeFlags(recent, stale, allFlagKeys)
} else {
totalFlags = recent.flagStorage
}
totalFlags = _resetFlags(totalFlags)
recent.flagStorage = totalFlags
state.dirty = dirty || needsUpload
state.cache = recent
// set local timestamp to smaller one if we don't have any changes
if (stale && recent && !state.dirty) {
state.cache._timestamp = Math.min(stale._timestamp, recent._timestamp)
}
state.flagStorage = state.cache.flagStorage
},
setFlag (state, { flag, value }) {
state.flagStorage[flag] = value
state.dirty = true
}
}
const serverSideStorage = {
state: {
...defaultState
},
mutations: {
setServerSideStorage (state, userData) {
const live = userData.storage
state.raw = live
let cache = state.cache
cache = _doMigrations(cache)
let { recent, stale, needsUpload } = _getRecentData(cache, live)
const userNew = userData.created_at > NEW_USER_DATE
const flagsTemplate = userNew ? newUserFlags : defaultState.defaultState
let dirty = false
if (recent === null) {
console.debug(`Data is empty, initializing for ${userNew ? 'new' : 'existing'} user`)
recent = _wrapData({
flagStorage: { ...flagsTemplate }
})
}
if (!needsUpload && recent && stale) {
console.debug('Checking if data needs merging...')
// discarding timestamps and versions
const { _timestamp: _0, _version: _1, ...recentData } = recent
const { _timestamp: _2, _version: _3, ...staleData } = stale
dirty = !isEqual(recentData, staleData)
console.debug(`Data ${dirty ? 'needs' : 'doesn\'t need'} merging`)
}
const allFlagKeys = _getAllFlags(recent, stale)
let totalFlags
if (dirty) {
// Merge the flags
console.debug('Merging the flags...')
totalFlags = _mergeFlags(recent, stale, allFlagKeys)
} else {
totalFlags = recent.flagStorage
}
// This does side effects on totalFlags !!!
// only resets if needed (checks are inside)
_resetFlags(totalFlags, allFlagKeys)
recent.flagStorage = totalFlags
state.dirty = dirty || needsUpload
state.cache = recent
state.flagStorage = state.cache.flagStorage
},
setFlag (state, { flag, value }) {
state.flagStorage[flag] = value
state.dirty = true
}
...cloneDeep(defaultState)
},
mutations,
actions: {
pushServerSideStorage ({ state, rootState, commit }, { force = false } = {}) {
const needPush = state.dirty || force