From dd67dafef1622118d6f0d631b9745a4387a978e8 Mon Sep 17 00:00:00 2001 From: Tue Nguyen Date: Wed, 13 Apr 2022 10:36:41 -0400 Subject: [PATCH] Update Feed in parser service to talk to Supabase instead of Redis - Change any invalid/flagged feed code to use supabase - Add more methods to supabase modules - Add supabase mock test - Fix unit/e2e test accordingly --- docker/development.yml | 2 +- src/api/parser/env.local | 7 +- src/api/parser/src/data/feed.js | 14 ++- src/api/parser/src/index.js | 2 +- src/api/parser/src/parser.js | 2 +- .../parser/src/utils/__mocks__/supabase.js | 73 ++++++++++++++ src/api/parser/src/utils/storage.js | 40 +------- src/api/parser/src/utils/supabase.js | 92 +++++++++++++++++ src/api/parser/test/e2e/parser-flow.test.js | 98 ++++++------------- src/api/parser/test/feed-processor.test.js | 6 ++ src/api/parser/test/feed.test.js | 44 +++++---- src/api/parser/test/post.test.js | 7 ++ src/api/parser/test/storage.test.js | 30 ++---- 13 files changed, 256 insertions(+), 161 deletions(-) create mode 100644 src/api/parser/src/utils/__mocks__/supabase.js diff --git a/docker/development.yml b/docker/development.yml index b49f6c0c9a..4c9d1db5cc 100644 --- a/docker/development.yml +++ b/docker/development.yml @@ -81,7 +81,7 @@ services: # In development and testing, the SSO service needs to contact the Supabase # service directly via Docker vs through the http://localhost/v1/supabase domain. # Using staging database - - SUPABASE_URL=https://dev.api.telescope.cdot.systems/v1/supabase + - SUPABASE_URL=http://kong:8000 depends_on: - elasticsearch - traefik diff --git a/src/api/parser/env.local b/src/api/parser/env.local index 813711ac1b..5e417c1675 100644 --- a/src/api/parser/env.local +++ b/src/api/parser/env.local @@ -38,6 +38,7 @@ PARSER_PORT = 10000 ################################################################################ # Supabase Secrets -# Using staging database -#SUPABASE_URL=http://localhost/v1/supabase -SUPABASE_URL=https://dev.supabase.telescope.cdot.systems/ +# Staging database +#SUPABASE_URL=https://dev.supabase.telescope.cdot.systems/ +SUPABASE_URL=http://localhost/v1/supabase + diff --git a/src/api/parser/src/data/feed.js b/src/api/parser/src/data/feed.js index 564f6007f3..b5e1f3be84 100644 --- a/src/api/parser/src/data/feed.js +++ b/src/api/parser/src/data/feed.js @@ -6,17 +6,21 @@ const { getFeeds, addFeed, removeFeed, - setInvalidFeed, - isInvalid, setDelayedFeed, isDelayed, removePost, getPost, getPosts, +} = require('../utils/storage'); + +const { + isInvalid, + setInvalidFeed, getFlaggedFeeds, setFlaggedFeed, unsetFlaggedFeed, -} = require('../utils/storage'); +} = require('../utils/supabase'); + const { deletePost } = require('../utils/indexer'); const urlToId = (url) => hash(normalizeUrl(url)); @@ -83,8 +87,8 @@ class Feed { * Adds the current Feed to the database with the specified reason * Returns a Promise */ - setInvalid(reason) { - return setInvalidFeed(this.id, reason); + setInvalid() { + return setInvalidFeed(this.id); } /** diff --git a/src/api/parser/src/index.js b/src/api/parser/src/index.js index 2ec36bde5e..e9676b6783 100644 --- a/src/api/parser/src/index.js +++ b/src/api/parser/src/index.js @@ -18,7 +18,7 @@ feedQueue.on('drained', loadFeedsIntoQueue); */ feedQueue.on('failed', (job, err) => invalidateFeed(job.data.id, err).catch((error) => - logger.error({ error }, 'Unable to invalidate feed') + logger.error({ error }, `Unable to invalidate feed ${job.data.id}`) ) ); diff --git a/src/api/parser/src/parser.js b/src/api/parser/src/parser.js index 1142bcddce..503d625d6c 100644 --- a/src/api/parser/src/parser.js +++ b/src/api/parser/src/parser.js @@ -33,7 +33,7 @@ const updateFeed = async (feedData) => { */ const invalidateFeed = async (id, error) => { const feed = await Feed.byId(id); - await feed.setInvalid(error.message); + await feed.setInvalid(); logger.info(`Invalidating feed ${feed.url} for the following reason: ${error.message}`); }; diff --git a/src/api/parser/src/utils/__mocks__/supabase.js b/src/api/parser/src/utils/__mocks__/supabase.js new file mode 100644 index 0000000000..e756dd326d --- /dev/null +++ b/src/api/parser/src/utils/__mocks__/supabase.js @@ -0,0 +1,73 @@ +const { hash } = require('@senecacdot/satellite'); +const normalizeUrl = require('normalize-url'); + +const urlToId = (url) => hash(normalizeUrl(url)); + +let feeds = []; +let feedIds = new Set(); + +module.exports = { + __resetMockFeeds: () => { + feeds = []; + feedIds = new Set(); + }, + /** + * @param {Array} feedObjects + */ + __setMockFeeds: (feedObjects) => { + const mockFeeds = feedObjects.reduce((uniqueFeeds, feed) => { + const id = feed.id || urlToId(feed.url); + if (!feedIds.has(id)) { + feedIds.add(id); + return uniqueFeeds.concat({ id, invalid: false, flagged: false }); + } + return uniqueFeeds; + }, []); + feeds = feeds.concat(mockFeeds); + }, + + // Invalid feed related functions + setInvalidFeed: (id) => { + feeds.forEach((feed) => { + if (feed.id === id) { + feed.invalid = true; + } + }); + return Promise.resolve(); + }, + getInvalidFeeds: () => { + const invalidFeedIds = feeds.filter((feed) => feed.flagged).map((feed) => ({ id: feed.id })); + return Promise.resolve(invalidFeedIds); + }, + isInvalid: (id) => { + const targetFeed = feeds.find((feed) => feed.id === id); + return Promise.resolve(!!targetFeed.invalid); + }, + // Flagged feed related functions + getAllFeeds: jest.fn().mockImplementation(() => Promise.resolve(feeds)), + setFlaggedFeed: jest.fn().mockImplementation((id) => { + feeds.forEach((feed) => { + if (feed.id === id) { + feed.flagged = true; + } + }); + return Promise.resolve(); + }), + unsetFlaggedFeed: jest.fn().mockImplementation((id) => { + feeds.forEach((feed) => { + if (feed.id === id) { + feed.flagged = false; + } + }); + return Promise.resolve(); + }), + + getFlaggedFeeds: jest.fn().mockImplementation(() => { + const flaggedFeedIds = feeds.filter((feed) => feed.flagged).map((feed) => feed.id); + return Promise.resolve(flaggedFeedIds); + }), + isFlagged: jest.fn().mockImplementation((id) => { + const targetFeed = feeds.find((feed) => feed.id === id); + return Promise.resolve(!!targetFeed.flagged); + }), +}; diff --git a/src/api/parser/src/utils/storage.js b/src/api/parser/src/utils/storage.js index 7fa975af2d..45767da5a3 100644 --- a/src/api/parser/src/utils/storage.js +++ b/src/api/parser/src/utils/storage.js @@ -1,26 +1,24 @@ const { logger, Redis } = require('@senecacdot/satellite'); +const { isFlagged } = require('./supabase'); const redis = Redis(); // Redis Keys const feedsKey = 't:feeds'; -const flaggedFeedsKey = 't:feeds:flagged'; const postsKey = 't:posts'; // Namespaces const feedNamespace = 't:feed:'; const postNamespace = 't:post:'; // Suffixes -const invalidSuffix = ':invalid'; const delayedSuffix = ':delayed'; // "6Xoj0UXOW3" to "t:post:6Xoj0UXOW3" const createPostKey = (id) => postNamespace.concat(id); // "NirlSYranl" to "t:feed:NirlSYranl" const createFeedKey = (id) => feedNamespace.concat(id); -// "NirlSYranl" to "t:feed:NirlSYranl:invalid" -const createInvalidFeedKey = (id) => createFeedKey(id).concat(invalidSuffix); + // "NirlSYranl" to "t:feed:NirlSYranl:delayed" const createDelayedFeedKey = (id) => createFeedKey(id).concat(delayedSuffix); @@ -50,7 +48,7 @@ module.exports = { addFeed: async (feed) => { // Check if feed being added already exists in flagged feeds set // If it is, do nothing - if (await redis.sismember(flaggedFeedsKey, feed.id)) return; + if (await isFlagged(feed.id)) return; const key = createFeedKey(feed.id); await redis @@ -78,20 +76,6 @@ module.exports = { getFeeds: () => redis.smembers(feedsKey), - getInvalidFeeds: async () => { - const invalidKeys = await getFeedKeysUsingScanStream(`${feedNamespace}*${invalidSuffix}`); - return Promise.all( - invalidKeys.map(async (key) => { - const reason = await redis.get(key); - const id = key.replace(feedNamespace, '').replace(invalidSuffix, ''); - return { - id, - reason: reason.replace(/\n/g, ' '), - }; - }) - ); - }, - getDelayedFeeds: async () => { const delayedKeys = await getFeedKeysUsingScanStream(`${feedNamespace}*${delayedSuffix}`); return delayedKeys.map((key) => { @@ -102,31 +86,21 @@ module.exports = { }); }, - getFlaggedFeeds: () => redis.smembers(flaggedFeedsKey), - getFeed: (id) => redis.hgetall(feedNamespace.concat(id)), getFeedsCount: () => redis.scard(feedsKey), - setInvalidFeed: (id, reason) => { - const key = createInvalidFeedKey(id); - const sevenDaysInSeconds = 60 * 60 * 24 * 7; // Expire after 7 days - return redis.set(key, reason, 'EX', sevenDaysInSeconds); - }, - /** * Removes a feed entry from redis * @param id id of feed to be removed */ removeFeed: async (id) => { const key = createFeedKey(id); - // Checks which set the feed is currently in - const redisKey = (await redis.sismember(feedsKey, id)) ? feedsKey : flaggedFeedsKey; try { await redis .multi() .hdel(key, 'id', 'author', 'url', 'user', 'link', 'etag', 'lastModified') - .srem(redisKey, id) + .srem(feedsKey, id) .exec(); } catch (error) { logger.error({ error }, `Error removing Feed ${id} from Redis`); @@ -134,12 +108,6 @@ module.exports = { } }, - setFlaggedFeed: (id) => redis.smove(feedsKey, flaggedFeedsKey, id), - - unsetFlaggedFeed: (id) => redis.smove(flaggedFeedsKey, feedsKey, id), - - isInvalid: (id) => redis.exists(createInvalidFeedKey(id)), - setDelayedFeed: (id, seconds) => redis.set(createDelayedFeedKey(id), seconds, 'EX', seconds), isDelayed: (id) => redis.exists(createDelayedFeedKey(id)), diff --git a/src/api/parser/src/utils/supabase.js b/src/api/parser/src/utils/supabase.js index 47398ce018..673526affd 100644 --- a/src/api/parser/src/utils/supabase.js +++ b/src/api/parser/src/utils/supabase.js @@ -1,5 +1,7 @@ const { logger } = require('@senecacdot/satellite'); +const hash = require('@senecacdot/satellite/src/hash'); const { createClient } = require('@supabase/supabase-js'); +const normalizeUrl = require('normalize-url'); const { SUPABASE_URL, SERVICE_ROLE_KEY } = process.env; @@ -27,4 +29,94 @@ module.exports = { url: feed.url, })); }, + + // Invalid feed related functions + async setInvalidFeed(id) { + const { error } = await supabase.from('feeds').update({ invalid: true }).eq('id', id); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't invalidate feed ${id} in supabase`); + } + }, + + async getInvalidFeeds() { + const { data: invalidFeeds, error } = await supabase.from('feeds').select().is('invalid', true); + if (error) { + logger.error({ error }); + throw Error(error.message, "can't fetch invalid feeds in supabase"); + } + return invalidFeeds; + }, + async isInvalid(id) { + const { data: invalidFeed, error } = await supabase + .from('feeds') + .select('invalid') + .eq('id', id) + .limit(1); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't fetch feed ${id} from supabase`); + } + return invalidFeed.invalid; + }, + + // Flagged feed related functions + async setFlaggedFeed(id) { + const { error } = await supabase.from('feeds').update({ flagged: true }).eq('id', id); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't flag feed ${id} in supabase`); + } + }, + async unsetFlaggedFeed(id) { + const { error } = await supabase.from('feeds').update({ flagged: false }).eq('id', id); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't unflag feed ${id} in supabase`); + } + }, + async getFlaggedFeeds() { + const { data: flaggedFeeds, error } = await supabase.from('feeds').select().eq('flagged', true); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't flagged feeds from supabase`); + } + return flaggedFeeds.map((feed) => feed.id); + }, + async isFlagged(id) { + const { data: flaggedFeed, error } = await supabase + .from('feeds') + .select('flagged') + .eq('id', id) + .limit(1); + + if (error) { + logger.error({ error }); + throw Error(error.message, `can't fetch feed ${id} from supabase`); + } + return flaggedFeed.flagged; + }, + async addFeeds(feeds) { + const { data, error } = await supabase.from('feeds').insert( + feeds.map((feed) => ({ + url: feed.url, + id: hash(normalizeUrl(feed.url)), + wiki_author_name: feed.author, + invalid: false, + flagged: false, + type: 'blog', + html_url: null, + user_id: null, + })) + ); + if (error) { + logger.error({ error }); + throw Error(error.message, "can't insert feeds to supabase"); + } + }, }; diff --git a/src/api/parser/test/e2e/parser-flow.test.js b/src/api/parser/test/e2e/parser-flow.test.js index 5e9b7f94cf..0966176c5e 100644 --- a/src/api/parser/test/e2e/parser-flow.test.js +++ b/src/api/parser/test/e2e/parser-flow.test.js @@ -1,27 +1,13 @@ -const { hash, fetch, logger, Satellite } = require('@senecacdot/satellite'); +const { hash, logger, Satellite } = require('@senecacdot/satellite'); const normalizeUrl = require('normalize-url'); const { loadFeedsIntoQueue, invalidateFeed } = require('../../src/parser'); const feedWorker = require('../../src/feed/worker'); const { feedQueue } = require('../../src/feed/queue'); -const { getAllFeeds } = require('../../src/utils/supabase'); +const { addFeeds, getInvalidFeeds, getAllFeeds } = require('../../src/utils/supabase'); const urlToId = (url) => hash(normalizeUrl(url)); -jest.mock('../../src/utils/supabase', () => ({ - ...jest.requireActual('../../src/utils/supabase'), - getAllFeeds: jest.fn(), -})); - -const fetchData = async (url) => { - const res = await fetch(url); - const data = await res.json(); - return data; -}; - -// return true if check array has all elements that exist in target array -const checker = (target, check) => check.every((value) => target.includes(value)); - // resolve once feedQueue has finished processing all jobs const waitForDrained = () => new Promise((resolve) => { @@ -38,69 +24,41 @@ const processFeeds = () => { }; let satellite; - +const valid = [ + { + author: 'Tue Nguyen', + url: 'http://localhost:8888/feed.xml', + }, +]; +const invalid = [ + { + author: 'John Doe', + url: 'https://johnhasinvalidfeed.com/feed', + }, + { + author: 'Jane Doe', + url: 'https://janehasinvalidfeed.com/feed', + }, +]; beforeAll(async () => { satellite = new Satellite(); await feedQueue.empty(); // remove jobs from the queue + await addFeeds([...valid, ...invalid]); await processFeeds(); // start the feed queue for e2e test }); afterAll(() => Promise.all([feedQueue.close(), satellite.stop()])); - describe("Testing parser service's flow", () => { - test('Parser should process 2 valid feeds', async () => { - const valid = [ - { - author: 'Tue Nguyen', - url: 'http://localhost:8888/feed.xml', - }, - { - author: 'Antonio Bennett', - url: 'http://localhost:8888/feed.xml', - }, - ]; - getAllFeeds.mockImplementation(() => Promise.resolve(valid)); - loadFeedsIntoQueue(); - await waitForDrained(); - - const allFeeds = await fetchData(`${process.env.POSTS_URL}/feeds`); - const validFeeds = await Promise.all(allFeeds.map((feed) => fetchData(feed.url))); - const validIds = validFeeds.map((feed) => feed.id); - - expect(allFeeds.length).toBeGreaterThan(0); - expect( - checker( - validIds, - valid.map((elem) => urlToId(elem.url)) - ) - ).toBe(true); // check if feeds from Posts service contains all valid feeds - }); - - test('Parser should process 2 invalid feeds', async () => { - const invalid = [ - { - author: 'John Doe', - url: 'https://johnhasinvalidfeed.com/feed', - }, - { - author: 'Jane Doe', - url: 'https://janehasinvalidfeed.com/feed', - }, - ]; - getAllFeeds.mockImplementation(() => Promise.resolve(invalid)); + test('Parser should process 1 valid feed and 2 invalid feeds', async () => { loadFeedsIntoQueue(); await waitForDrained(); - - const feeds = await fetchData(`${process.env.POSTS_URL}/feeds/invalid`); - const invalidFeeds = await Promise.all(feeds.map((feed) => fetchData(feed.url))); - const invalidIds = invalidFeeds.map((feed) => feed.id); - - expect(invalidFeeds.length).toBeGreaterThan(0); - expect( - checker( - invalidIds, - invalid.map((elem) => urlToId(elem.url)) - ) - ).toBe(true); // check if feeds from Posts service contains all invalid feeds + const allFeeds = await getAllFeeds(); + const invalidFeeds = await getInvalidFeeds(); + const invalidIds = invalid.map((feed) => urlToId(feed.url)); + + expect(allFeeds.length).toBe(3); + expect(invalidFeeds.length).toBe(2); + expect(invalidIds.includes(invalidFeeds[0].id)).toBe(true); + expect(invalidIds.includes(invalidFeeds[1].id)).toBe(true); }); }); diff --git a/src/api/parser/test/feed-processor.test.js b/src/api/parser/test/feed-processor.test.js index 75e076ee20..aca79fbeba 100644 --- a/src/api/parser/test/feed-processor.test.js +++ b/src/api/parser/test/feed-processor.test.js @@ -3,6 +3,8 @@ const processor = require('../src/feed/processor'); const Feed = require('../src/data/feed'); jest.mock('../src/utils/indexer'); +jest.mock('../src/utils/supabase'); +const { __setMockFeeds } = require('../src/utils/supabase'); describe('Feed Processor Tests', () => { const createFeed = (url) => @@ -13,6 +15,7 @@ describe('Feed Processor Tests', () => { test('Passing a valid Atom feed URI should pass', async () => { const url = fixtures.getAtomUri(); + __setMockFeeds([{ url }]); const id = await createFeed(url); fixtures.nockValidAtomResponse(); const job = fixtures.createMockJobObjectFromFeedId(id); @@ -21,6 +24,7 @@ describe('Feed Processor Tests', () => { test('Passing a valid RSS feed URI should pass', async () => { const url = fixtures.getRssUri(); + __setMockFeeds([{ url }]); const id = await createFeed(url); fixtures.nockValidRssResponse(); const job = fixtures.createMockJobObjectFromFeedId(id); @@ -29,6 +33,7 @@ describe('Feed Processor Tests', () => { test('Passing an invalid RSS category feed should pass', async () => { const url = fixtures.getRssUri(); + __setMockFeeds([{ url }]); const id = await createFeed(url); fixtures.nockInvalidRssResponse(); const job = fixtures.createMockJobObjectFromFeedId(id); @@ -37,6 +42,7 @@ describe('Feed Processor Tests', () => { test('Passing a valid RSS category feed should pass', async () => { const url = fixtures.getRssUri(); + __setMockFeeds([{ url }]); const id = await createFeed(url); fixtures.nockValidRssResponse(); const job = fixtures.createMockJobObjectFromFeedId(id); diff --git a/src/api/parser/test/feed.test.js b/src/api/parser/test/feed.test.js index a8d6ef77e8..b0ba29394d 100644 --- a/src/api/parser/test/feed.test.js +++ b/src/api/parser/test/feed.test.js @@ -7,6 +7,9 @@ const { search } = require('../src/utils/indexer'); const urlToId = (url) => hash(normalizeUrl(url)); +jest.mock('../src/utils/supabase'); +const { __setMockFeeds, __resetMockFeeds } = require('../src/utils/supabase'); + describe('Post data class tests', () => { const data = { author: 'Post Author', @@ -92,7 +95,11 @@ describe('Post data class tests', () => { describe('Get and Set Feed objects from database', () => { const { mock } = Elastic(); - beforeAll(() => createFeed().save()); + beforeAll(async () => { + const feed = new Feed(data.author, data.url, data.user, data.link, null, null); + __setMockFeeds([feed]); + await feed.save(); + }); afterEach(() => mock.clearAll()); test('Feed.byId() for a valid id should return a Feed', async () => { @@ -128,6 +135,7 @@ describe('Post data class tests', () => { test('Setting etag on a feed should persist', async () => { const feed = new Feed('author', 'http://url.com/with/etag'); feed.etag = 'etag'; + __setMockFeeds([feed]); await feed.save(); const persisted = await Feed.byId(feed.id); expect(persisted.etag).toEqual('etag'); @@ -139,6 +147,7 @@ describe('Post data class tests', () => { test('Setting lastModified on a feed should persist', async () => { const feed = new Feed('author', 'http://url.com/with/lastModified'); feed.lastModified = 'lastModified'; + __setMockFeeds([feed]); await feed.save(); const persisted = await Feed.byId(feed.id); expect(persisted.lastModified).toEqual('lastModified'); @@ -158,6 +167,7 @@ describe('Post data class tests', () => { const feed = new Feed(data.author, data.url, data.user, data.link, null, null); feed.author = 'Author Post'; feed.url = 'https://modified.user.feed.com/feed.rss'; + __setMockFeeds([feed]); await feed.update(); const modifiedFeed = await Feed.byId(feed.id); // modifiedFeed.id should be different from feed.id as feed hasn't been reassigned to updated feed.id value @@ -236,7 +246,7 @@ describe('Post data class tests', () => { let feed = new Feed(data.author, data.url, data.user, data.link, 'etag', 'lastModified'); let feed2 = new Feed(data2.author, data2.url, data2.user, data2.link, 'etag', 'lastModified'); let feed3 = new Feed(data3.author, data3.url, data3.user, data3.link, 'etag', 'lastModified'); - + __setMockFeeds([feed, feed2, feed3]); feed = await Feed.byId(await Feed.create(feed)); feed2 = await Feed.byId(await Feed.create(feed2)); feed3 = await Feed.byId(await Feed.create(feed3)); @@ -262,54 +272,46 @@ describe('Post data class tests', () => { // Teardown removing the added feed await Promise.all([await feed.delete(), await feed2.delete(), await feed3.delete()]); }); + }); + describe('Set and get flagged feeds objects from Supabase', () => { + beforeAll(() => { + __resetMockFeeds(); + }); - test('Flagged feed should appear in t:feeds:flagged and not t:feeds', async () => { + test('Flagged feed should be set and retrieved correctly', async () => { const feedData = new Feed(data.author, data.url, data.user, data.link); const feedData2 = new Feed(data2.author, data2.url, data2.user, data2.link); const feedData3 = new Feed(data3.author, data3.url, data3.user, data3.link); + __setMockFeeds([feedData, feedData2, feedData3]); // Check all three feeds are created const feed = await Feed.byId(await Feed.create(feedData)); const feed2 = await Feed.byId(await Feed.create(feedData2)); const feed3 = await Feed.byId(await Feed.create(feedData3)); - let unFlaggedFeeds = await Feed.all(); + const unFlaggedFeeds = await Feed.all(); expect(unFlaggedFeeds.length).toBe(3); // Test flag() await Promise.all([feed.flag(), feed2.flag()]); - unFlaggedFeeds = await Feed.all(); - expect(unFlaggedFeeds.length).toBe(1); - let flaggedFeeds = await Feed.flagged(); expect(flaggedFeeds.length).toBe(2); - // Feed should not appear in unflagged set if Feed is flagged and added again - await Feed.create(feedData); - unFlaggedFeeds = await Feed.all(); - expect(unFlaggedFeeds.length).toBe(1); - // Flagged feeds should have same data as feed + feed2 expect(flaggedFeeds.some((flaggedFeed) => flaggedFeed.id === feed.id)).toBe(true); expect(flaggedFeeds.some((flaggedFeed) => flaggedFeed.id === feed2.id)).toBe(true); // Test unflag(); await feed2.unflag(); - unFlaggedFeeds = await Feed.all(); - expect(unFlaggedFeeds.length).toBe(2); - flaggedFeeds = await Feed.flagged(); expect(flaggedFeeds.length).toBe(1); - // Testing delete() as part of teardown, feed should be removed from t:feeds:flagged - await feed.delete(); + await feed.unflag(); flaggedFeeds = await Feed.flagged(); expect(flaggedFeeds.length).toBe(0); - await feed2.delete(); - await feed3.delete(); - // Testing whether removing an already removed feed will error - await feed.delete(); + // Testing delete() as part of teardown, feed should be removed from t:feeds:flagged + await Promise.all([await feed.delete(), await feed2.delete(), await feed3.delete()]); }); }); }); diff --git a/src/api/parser/test/post.test.js b/src/api/parser/test/post.test.js index 305dffada4..9c993d5583 100644 --- a/src/api/parser/test/post.test.js +++ b/src/api/parser/test/post.test.js @@ -26,6 +26,8 @@ const Post = require('../src/data/post'); const Feed = require('../src/data/feed'); jest.mock('../src/utils/indexer'); +jest.mock('../src/utils/supabase'); +const { __setMockFeeds } = require('../src/utils/supabase'); describe('Post data class tests', () => { let feed; @@ -42,6 +44,7 @@ describe('Post data class tests', () => { }; beforeAll(async () => { + __setMockFeeds([{ url: 'http://feed-url.com/' }]); const id = await Feed.create({ author: 'Feed Author', url: 'http://feed-url.com/', @@ -136,6 +139,7 @@ describe('Post data class tests', () => { }); test('Post.create() should be able to parse an Object into a Post', async () => { + __setMockFeeds([{ url: data.url }]); const id = await Post.create(data); const expectedId = 'a371654c75'; expect(id).toEqual(expectedId); @@ -145,6 +149,7 @@ describe('Post data class tests', () => { }); test('Posts should have a (dynamic) text property', async () => { + __setMockFeeds([{ url: data.url }]); const id = await Post.create(data); const post = await Post.byId(id); expect(post.text).toEqual(text); @@ -154,6 +159,7 @@ describe('Post data class tests', () => { const missingData = { ...data, updated: null, feed }; // Make sure that updated was turned into a date + __setMockFeeds([{ url: missingData.url }]); const id = await Post.create(missingData); const parsed = await Post.byId(id); expect(parsed.updated).toBeDefined(); @@ -161,6 +167,7 @@ describe('Post data class tests', () => { }); test('Post.save() and Post.byId() should both work as expected', async () => { + __setMockFeeds([{ url: data.url }]); const id = await Post.create(data); const post = await Post.byId(id); expect(post).toEqual(data); diff --git a/src/api/parser/test/storage.test.js b/src/api/parser/test/storage.test.js index cbcc0ce20c..5afe07e98b 100644 --- a/src/api/parser/test/storage.test.js +++ b/src/api/parser/test/storage.test.js @@ -3,20 +3,19 @@ const { addFeed, getFeed, getFeeds, - getFlaggedFeeds, getFeedsCount, removeFeed, - setFlaggedFeed, - unsetFlaggedFeed, addPost, getPost, getPosts, getPostsCount, removePost, } = require('../src/utils/storage'); - const Feed = require('../src/data/feed'); +jest.mock('../src/utils/supabase'); +const { __setMockFeeds } = require('../src/utils/supabase'); + describe('Storage tests for feeds', () => { const feed1 = new Feed('James Smith', 'http://seneca.co/jsmith', 'user'); const feed2 = new Feed('James Smith 2', 'http://seneca.co/jsmith/2', 'user'); @@ -30,7 +29,10 @@ describe('Storage tests for feeds', () => { 'last-modified' ); - beforeAll(() => Promise.all([addFeed(feed1), addFeed(feed2), addFeed(feed3), addFeed(feed4)])); + beforeAll(async () => { + __setMockFeeds([feed1, feed2, feed3, feed4]); + await Promise.all([addFeed(feed1), addFeed(feed2), addFeed(feed3), addFeed(feed4)]); + }); it('should allow retrieving a feed by id after inserting', async () => { const feed = await getFeed(feed1.id); @@ -85,24 +87,6 @@ describe('Storage tests for feeds', () => { expect(removedFeed.id).toBe(undefined); expect(feeds.includes(feed.id)).toBe(false); }); - - it('feed3 should appear in flaggedFeed set after being flagged', async () => { - const feed = await getFeed(feed3.id); - await setFlaggedFeed(feed3.id); - const feeds = await getFeeds(); - const flaggedFeeds = await getFlaggedFeeds(); - expect(flaggedFeeds.includes(feed.id)).toBe(true); - expect(feeds.includes(feed.id)).toBe(false); - }); - - it('feed3 should not appear in flaggedFeed set after being unflagged', async () => { - const feed = await getFeed(feed3.id); - await unsetFlaggedFeed(feed3.id); - const feeds = await getFeeds(); - const flaggedFeeds = await getFlaggedFeeds(); - expect(feeds.includes(feed.id)).toBe(true); - expect(flaggedFeeds.includes(feed.id)).toBe(false); - }); }); describe('Storage tests for posts', () => {