Skip to content

Commit

Permalink
Fix mock for glob, fix infinite loop
Browse files Browse the repository at this point in the history
  • Loading branch information
pvdz committed Feb 21, 2020
1 parent 9948996 commit 2b79805
Show file tree
Hide file tree
Showing 3 changed files with 158 additions and 22 deletions.
63 changes: 62 additions & 1 deletion packages/gatsby/src/redux/__tests__/__snapshots__/index.js.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`redux db should write cache to disk 1`] = `
exports[`redux db should write loki cache to disk 1`] = `
Object {
"componentDataDependencies": Object {
"connections": Map {},
Expand Down Expand Up @@ -28,3 +28,64 @@ Object {
"webpackCompilationHash": "",
}
`;

exports[`redux db should write redux cache to disk 1`] = `
Object {
"componentDataDependencies": Object {
"connections": Map {},
"nodes": Map {},
},
"components": Map {
"/Users/username/dev/site/src/templates/my-sweet-new-page.js" => Object {
"componentPath": "/Users/username/dev/site/src/templates/my-sweet-new-page.js",
"isInBootstrap": true,
"pages": Set {
"/my-sweet-new-page/",
},
"query": "",
},
},
"jobsV2": Object {
"complete": Map {},
"incomplete": Map {},
},
"nodes": Map {
"pageA" => Object {
"id": "pageA",
"internal": Object {
"type": "Ding",
},
},
"pageB" => Object {
"id": "pageB",
"internal": Object {
"type": "Dong",
},
},
},
"nodesByType": Map {
"Ding" => Map {
"pageA" => Object {
"id": "pageA",
"internal": Object {
"type": "Ding",
},
},
},
"Dong" => Map {
"pageB" => Object {
"id": "pageB",
"internal": Object {
"type": "Dong",
},
},
},
},
"pageDataStats": Map {},
"staticQueryComponents": Map {},
"status": Object {
"plugins": Object {},
},
"webpackCompilationHash": "",
}
`;
105 changes: 89 additions & 16 deletions packages/gatsby/src/redux/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,47 @@ jest.mock(`fs-extra`, () => {
removeSync: jest.fn(file => mockWrittenContent.delete(file)),
}
})
jest.mock(`glob`, () => {
return {
sync: jest.fn(pattern => {
// Tricky.
// Expecting a path prefix, ending with star. Else this won't work :/
if (pattern.slice(-1) !== `*`) {
throw new Error(`Expected pattern ending with star`)
}
let globPrefix = pattern.slice(0, -1)
if (globPrefix.includes(`*`)) {
throw new Error(`Expected pattern to be a prefix`)
}
const files = []
mockWrittenContent.forEach((value, key) => {
if (key.startsWith(globPrefix)) {
files.push(key)
}
})
return files
}),
}
})

function getFakeNodes() {
// Set nodes to something or the cache will fail because it asserts this
// Actual nodes content should match TS type; these are verified
let map /*: Map<string, IReduxNode>*/ = new Map()
map.set(`pageA`, {
id: `pageA`,
internal: {
type: `Ding`,
},
})
map.set(`pageB`, {
id: `pageB`,
internal: {
type: `Dong`,
},
})
return map
}

describe(`redux db`, () => {
const initialComponentsState = _.cloneDeep(store.getState().components)
Expand All @@ -70,29 +111,61 @@ describe(`redux db`, () => {
mockWrittenContent.clear()
})

it(`should write cache to disk`, async () => {
expect(initialComponentsState).toEqual(new Map())
// yuck - loki and redux will have different shape of redux state (nodes and nodesByType)
// Note: branched skips will keep snapshots with and without loki env var
if (process.env.GATSBY_DB_NODES === `loki`) {
it.skip(`should write redux cache to disk`, async () => {})
it(`should write loki cache to disk`, async () => {
expect(initialComponentsState).toEqual(new Map())

await saveState()
store.getState().nodes = getFakeNodes()

expect(writeToCache).toBeCalled()
await saveState()

// reset state in memory
store.dispatch({
type: `DELETE_CACHE`,
expect(writeToCache).toBeCalled()

// reset state in memory
store.dispatch({
type: `DELETE_CACHE`,
})
// make sure store in memory is empty
expect(store.getState().components).toEqual(initialComponentsState)

// read data that was previously cached
const data = readState()

// make sure data was read and is not the same as our clean redux state
expect(data.components).not.toEqual(initialComponentsState)

expect(_.omit(data, [`nodes`, `nodesByType`])).toMatchSnapshot()
})
// make sure store in memory is empty
expect(store.getState().components).toEqual(initialComponentsState)
} else {
it.skip(`should write loki cache to disk`, async () => {})
it(`should write redux cache to disk`, async () => {
expect(initialComponentsState).toEqual(new Map())

// read data that was previously cached
const data = readState()
store.getState().nodes = getFakeNodes()

// make sure data was read and is not the same as our clean redux state
expect(data.components).not.toEqual(initialComponentsState)
await saveState()

// yuck - loki and redux will have different shape of redux state (nodes and nodesByType)
expect(_.omit(data, [`nodes`, `nodesByType`])).toMatchSnapshot()
})
expect(writeToCache).toBeCalled()

// reset state in memory
store.dispatch({
type: `DELETE_CACHE`,
})
// make sure store in memory is empty
expect(store.getState().components).toEqual(initialComponentsState)

// read data that was previously cached
const data = readState()

// make sure data was read and is not the same as our clean redux state
expect(data.components).not.toEqual(initialComponentsState)

expect(data).toMatchSnapshot()
})
}

it(`should drop legacy file if exists`, async () => {
expect(initialComponentsState).toEqual(new Map())
Expand Down
12 changes: 7 additions & 5 deletions packages/gatsby/src/redux/persist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,16 @@ export function readFromCache(): ICachedReduxState {

const nodes: [string, IReduxNode][] = [].concat(...chunks)

if (chunks.length) {
obj.nodes = new Map(nodes)
} else {
if (!chunks.length) {
report.info(
`Cache exists but contains no nodes. There should be at least some nodes available so it seems the cache was corrupted. Disregarding the cache and proceeding as if there was none.`
)
// TODO: this is a DeepPartial<ICachedReduxState> but requires a big change
return {} as ICachedReduxState
}

obj.nodes = new Map(nodes)

return obj
}

Expand All @@ -78,7 +78,7 @@ function guessSafeChunkSize(values: [string, IReduxNode][]): number {

const nodesToTest = 11 // Very arbitrary number
const valueCount = values.length
const step = Math.floor(valueCount / nodesToTest)
const step = Math.max(1, Math.ceil(valueCount / nodesToTest))
let maxSize = 0
for (let i = 0; i < valueCount; i += step) {
const size = v8.serialize(values[i]).length
Expand All @@ -99,6 +99,7 @@ function prepareCacheFolder(
// This prevents an OOM when the page nodes collectively contain to much data
const map = contents.nodes
contents.nodes = undefined

writeFileSync(reduxRestFile(targetDir), v8.serialize(contents))
// Now restore them on the redux store
contents.nodes = map
Expand All @@ -124,6 +125,7 @@ function safelyRenameToBak(reduxCacheFolder: string): string {
const tmpSuffix = `.bak`
let suffixCounter = 0
let bakName = reduxCacheFolder + tmpSuffix // Start without number

while (existsSync(bakName)) {
++suffixCounter
bakName = reduxCacheFolder + tmpSuffix + suffixCounter
Expand Down Expand Up @@ -166,7 +168,7 @@ export function writeToCache(contents: ICachedReduxState): void {
removeSync(bakName)
}
} catch (e) {
console.warn(
report.warn(
`Non-fatal: Deleting the old cache folder failed, left behind in \`${bakName}\`. Rimraf reported this error: ${e}`
)
}
Expand Down

0 comments on commit 2b79805

Please sign in to comment.