VOOZH about

URL: https://sammaye.wordpress.com/

⇱ Sammaye's Blog | Bow Chicka Bow Wow


Skip to content
👁 Image

Sammaye's Blog

Skip to content
👁 Image

So today I was awoken to the fact that my database had mysteriously lost data after putting out a new feature.

This a MongoDB database whereby, via the driver, you can replace documents.

After some digging I found that despite calling a function “updateEmployer” GitHub Co-Pilot had actually written the body of a function called “replaceEmployer” with only the fields in the C# schema file. I mean fair I didn’t know the database fields not defined in coding but that’s irrelevant, you would never use ReplaceOneAsync in an update function.

Since I accidentally trusted the AI without doing a proper review and it passed unit tests this went out and wiped the database…

Vibe coding is a fallacy and one you should avoid. This isn’t my first run in with AI incompetence it is just my last tbh

0
https://github.com/facebook/react-native/issues/29614#issuecomment-3069870022 this worked best for me:

const [behaviour, setBehaviour] = useState(‘height’);
useEffect(() => {
const showListener = Keyboard.addListener(‘keyboardDidShow’, () => {
setBehaviour(‘height’);
});
const hideListener = Keyboard.addListener(‘keyboardDidHide’, () => {
setBehaviour(undefined);
});

return () => {
showListener.remove();
hideListener.remove();
};
}, []);

….

<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === ‘android’ ? behaviour : undefined}
….
0
'use strict';

const MongoClient = require('mongodb').MongoClient;
const _ = require('lodash');

/**
 * @typedef IMongoStoreDocument
 * @extends import('mongodb').Document
 * @property {import('mongodb').ObjectId} _id
 * @property {number} counter
 * @property {Date} expirationDate
 * @property {string} [key]
 */

class MongoStore {
 /** @type {import('mongodb').MongoClient} */
 client;

 /** @type {{uri: string, user: string, password: string, authSource: string, collectionName: string}} */
 dbOptions;

 /** @type {import('mongodb').Collection} */
 collection;

 /** @type {number} */
 expireTimeMs;

 /** @type {boolean} */
 resetExpireDateOnChange;

 /** @type {Function} */
 errorHandler;

 /** @type {boolean} */
 createTtlIndex;

 /** @type {Object} */
 connectionOptions;

 /** @type {string} */
 prefix;

 /**
 * @constructor. Only required if the user needs to pass
 * some store specific parameters. For example, in a Mongo Store, the user will
 * need to pass the URI, username and password for the Mongo database.
 *
 * Accepting a custom `prefix` here is also recommended.
 *
 * @param options {{
 * collectionName: string,
 * uri: string,
 * user: string,
 * password: string,
 * authSource: string,
 * expireTimeMs: Number,
 * resetExpireDateOnChange: Boolean,
 * errorHandler: Function,
 * createTtlIndex: Boolean,
 * collection: import('mongodb').Collection,
 * connectionOptions: import('mongodb').MongoClientOptions,
 * authSource: string,
 * prefix: string,
 * }}
 */
 constructor(options) {
 const allOptions = _.defaults(options, {
 collectionName: 'expressRateRecords',
 expireTimeMs: 60000,
 resetExpireDateOnChange: false,
 errorHandler: _.noop,
 createTtlIndex: true,
 connectionOptions: {},
 prefix: '',
 });

 if (!allOptions.collection && (!allOptions.collectionName || !allOptions.uri)) {
 throw new Error('collection or collectionName and uri should be set');
 }

 this.dbOptions = {
 uri: allOptions.uri,
 user: allOptions.user,
 password: allOptions.password,
 authSource: allOptions.authSource,
 collectionName: allOptions.collectionName,
 };

 this.collection = allOptions.collection;
 this.expireTimeMs = allOptions.expireTimeMs;
 this.resetExpireDateOnChange = allOptions.resetExpireDateOnChange;
 this.errorHandler = allOptions.errorHandler;
 this.createTtlIndex = allOptions.createTtlIndex;
 this.connectionOptions = allOptions.connectionOptions;
 this.prefix = allOptions.prefix;
 }

 /**
 * @returns {Collection<Document>}
 * @private
 */
 async _getCollection() {
 if (!this.client) {
 const connectionOptions = this.connectionOptions;

 if (this.dbOptions.user && this.dbOptions.password) {
 const dbName = _.last(this.dbOptions.uri.split('/'));

 connectionOptions.authSource = this.dbOptions.authSource || dbName;
 connectionOptions.auth = {
 user: this.dbOptions.user,
 password: this.dbOptions.password,
 };
 }

 this.client = await MongoClient.connect(this.dbOptions.uri, connectionOptions);
 }

 const collection = this.client.db().collection(this.dbOptions.collectionName);
 if (this.createTtlIndex) {
 await collection.createIndex({ expirationDate: 1 }, { expireAfterSeconds: 0 });
 }
 return collection;
 }

 /**
 * Method that actually initializes the store. Must be synchronous.
 *
 * This method is optional, it will be called only if it exists.
 *
 * @param options {Partial<import('express-rate-limit').Options>} - The options used to setup express-rate-limit.
 *
 * @public
 */
 init(options) {
 this.expireTimeMs = options.windowMs;
 }

 /**
 * Method to prefix the keys with the given text.
 *
 * Call this from get, increment, decrement, resetKey, etc.
 *
 * @param key {string} - The key.
 *
 * @returns {string} - The text + the key.
 */
 prefixKey(key) {
 return `${this.prefix}${key}`;
 }

 /**
 * Method to fetch a client's hit count and reset time.
 *
 * @param key {string} - The identifier for a client.
 *
 * @returns {{totalHits: number, resetTime: Date}} - The number of hits and reset time for that client.
 *
 * @public
 */
 async get(key) {
 const collection = await this._getCollection();

 const expressRateRecord = await collection.findOne({ _id: key });

 return {
 totalHits: expressRateRecord ? expressRateRecord.counter : 0,
 resetTime: expressRateRecord ? expressRateRecord.expirationDate : new Date(),
 };
 }

 /**
 * Method to increment a client's hit counter.
 *
 * @param key {string} - The identifier for a client.
 *
 * @returns {{totalHits: number, resetTime: Date}} - The number of hits and reset time for that client.
 *
 * @public
 */
 async increment(key) {
 try {
 const collection = await this._getCollection();

 const modifier = {
 $inc: { counter: 1 },
 };
 const expirationDate = new Date(Date.now() + this.expireTimeMs);

 if (this.resetExpireDateOnChange) {
 modifier.$set = { expirationDate: expirationDate };
 } else {
 modifier.$setOnInsert = { expirationDate: expirationDate };
 }

 const /** @type IMongoStoreDocument */ expressRateRecord = await collection.findOneAndUpdate(
 { _id: key },
 modifier,
 {
 upsert: true,
 returnDocument: 'after',
 },
 );
 console.log('expressRateRecord', expressRateRecord);
 return {
 totalHits: expressRateRecord.counter, // A positive integer
 resetTime: expressRateRecord.expirationDate, // A JS `Date` object
 };
 } catch (err) {
 // call function again in case of duplicate key error
 if (err && err.code === 11000) {
 return this.increment(key);
 }

 this.errorHandler(err);
 throw err;
 }
 }

 /**
 * Method to decrement a client's hit counter.
 *
 * @param key {string} - The identifier for a client.
 *
 * @public
 */
 async decrement(key) {
 try {
 const collection = await this._getCollection();

 const modifier = {
 $inc: { counter: -1 },
 };
 const expirationDate = new Date(Date.now() + this.expireTimeMs);

 if (this.resetExpireDateOnChange) {
 modifier.$set = { expirationDate: expirationDate };
 } else {
 modifier.$setOnInsert = { expirationDate: expirationDate };
 }

 await collection.findOneAndUpdate({ _id: key }, modifier, {
 upsert: true,
 returnDocument: 'after',
 });
 } catch (err) {
 this.errorHandler(err);
 throw err;
 }
 }

 /**
 * Method to reset a client's hit counter.
 *
 * @param key {string} - The identifier for a client.
 *
 * @public
 */
 async resetKey(key) {
 try {
 const collection = await this._getCollection();
 await collection.deleteOne({ _id: key });
 } catch (err) {
 this.errorHandler(err);
 throw err;
 }
 }
}

module.exports = MongoStore;
0

Using this https://bojnansky.com/running-angular-tests-with-github-actions/ as a guide I made GitHub actions for angular testing, I used this action:

name: Run Tests
on:
 push:
 branches:
 - beta
 - main
jobs:
 build:
 runs-on: ubuntu-latest
 steps:
 - uses: actions/checkout@v2
 - name: Install Angular CLI and sass
 run: npm install -g @angular/cli@^14.0.0 sass
 - name: Install modules
 run: npm install --force
 - name: Run tests
 run: npm run test-ci

With the npm command in the link:

"test-ci": "ng test --browsers=ChromeHeadless --watch=false"
0
function handler(event) {
 const request = event.request;
 const response = event.response;
 const headers = response.headers;
 
 let original_uri = '';

 if (request.headers.hasOwnProperty('x-original-uri')) {
 original_uri = request.headers['x-original-uri'].value;
 }
 
 let referer = '';
 if (request.headers.hasOwnProperty('referer')) {
 referer = request.headers['referer'].value;
 }
 
 if (
 (
 // Super important! when PWA queries for new cache it actually does not send the URI, figured this out after lots of debugging
 !referer || !referer.includes('ngsw-worker.js')
 ) &&
 request.uri.includes('index.html') && 
 original_uri && 
 !original_uri.includes('/embedded')
 ) {
 response.headers['x-frame-options'] = {value: 'SAMEORIGIN'};
 }
 
 return response;
}
0

A good env:

A good env setup:

jest.config.js

module.exports = {
 preset: '@shelf/jest-mongodb',
 transform: { "^.+\\.[tj]s?(x)?$": ["ts-jest", {"rootDir": "."}] },
 testMatch: [ "**/tests/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)" ],
 setupFilesAfterEnv: ['<rootDir>/setup-jest.js'],
};

jest-mongodb-config.js

module.exports = {
 mongodbMemoryServerOptions: {
 binary: {
 version: '5.0.0',
 skipMD5: true,
 },
 autoStart: false,
 instance: {},
 },
};

A global file for running predictable tests:

setup-jest.js

import {afterAll, beforeAll} from "@jest/globals";
import {BatchInterceptor} from "@mswjs/interceptors";
import MongoDB from "./services/MongoDB";
import './config/app.js';
import {faker} from "@faker-js/faker";
import nodeInterceptors from '@mswjs/interceptors/presets/node'
import mongoose from "mongoose";

beforeAll(async () => {
 config.APP_ENV = 'JEST';

 config.MONGO_URL = global.__MONGO_URI__;
 config.EMAIL_DEFAULT_WELCOME_FROM_EMAIL = faker.internet.email();
 config.LOGS_ENABLE_SENTRY = false;
 config.LOGS_ENABLE_CLOUDWATCH = false;
 config.LOGS_ENABLE_CONSOLE = false;
 config.EMAIL_SES_HOST = 'https://email.eu-west-1.amazonaws.com/';

 await MongoDB.connect(config);
});

beforeEach(async () => {
 global.interceptor = new BatchInterceptor({
 name: 'my-interceptor',
 interceptors: nodeInterceptors,
 });

 interceptor.apply();

 interceptor.on('request', ({request, requestId}) => {
 // This allows us to fake sending emails via SES
 // TODO add one for SMTP
 if (request.url === config.EMAIL_SES_HOST) {
 request.respondWith(new Response(
 '<SendEmailResponse xmlns="http://ses.amazonaws.com/doc/2010-12-01/">' +
 '<SendEmailResult>' +
 '<MessageId>0102018e1161bc00-332b3f83-4295-4b94-912b-f3f983a8a1f9-000000</MessageId>' +
 '</SendEmailResult>' +
 '<ResponseMetadata>' +
 '<RequestId>24239c14-b187-4bea-b458-31a7c7d02a26</RequestId>' +
 '</ResponseMetadata>' +
 '</SendEmailResponse>',
 {
 status: 200,
 }
 ));
 }
 });

 const collections = await mongoose.connection.db.collections();
 for (const collection of collections) {
 await mongoose.connection.db.dropCollection(collection.collectionName);
 }
});

afterAll(async () => {
 await MongoDB.close();
});

I also tend to add some helper components, namely a base record creator like so:

BaseFixture.js

import mongoose from "mongoose";

export default class {
 static collectionName;

 static getBaseRecord() {
 return {};
 }

 static async createBaseRecords(records) {
 const recordsInserted = [];
 for (const record of records) {
 let recordInserted = {...this.getBaseRecord(), ...record};

 const res = await mongoose.connection.db.collection(this.collectionName)
 .insertOne(recordInserted);

 recordInserted._id = res.insertedId;
 recordsInserted.push(recordInserted);
 }

 return recordsInserted;
 }
}

UserFixture.js

import User from "../../models/User";
import {faker} from "@faker-js/faker";
import BaseFixture from "./BaseFixture";

export default class extends BaseFixture {
 static collectionName = User.collectionName();

 static getBaseRecord() {
 return {
 first_name: faker.person.firstName(),
 last_name: faker.person.lastName(),
 email: faker.internet.email().toLowerCase(),
 status: 'ACTIVATE',
 user_roles: ['grp_coach'],
 // password123
 password: '$2a$10$koNBPtKwiVjKkQbnnVBrAOdIjflMnC/OzYzy/OhXjOLPr9RSjwe4K',
 password_salt: '$2a$20$piAYssU0VGLwhWbZRhCT5e'
 };
 }
}

So an example unit test making use of these would be:

import {afterAll, beforeAll, describe, expect, it, test} from '@jest/globals';
import * as LoginController from '../../../controllers/LoginController';
import RequestMock from "../../mocks/RequestMock";
import ResponseMock from "../../mocks/ResponseMock";
import UserFixture from "../../fixtures/UserFixture";
import UserSubscriptionFixture from "../../fixtures/UserSubscriptionFixture";
import UnauthorizedError from "../../../exceptions/UnauthorizedError";

describe('Login Controller', () => {
 it('Should allow a user to login', async () => {
 const users = await UserFixture.createBaseRecords([{}]);
 const subs = await UserSubscriptionFixture.createBaseRecords([{
 user_id: users[0]._id,
 }])
 let response = ResponseMock.create();
 await LoginController.signIn(RequestMock.create({
 body: {
 Authenticate_type: 'FORM',
 email: users[0].email,
 password: 'password123',
 },
 portal: {
 type: 'COACH',
 }
 }), response);

 expect(response.statusCode).toEqual(200);
 });

 it ('Should not allow invalid credentials to login', async() => {
 const users = await UserFixture.createBaseRecords([{}]);
 const subs = await UserSubscriptionFixture.createBaseRecords([{
 user_id: users[0]._id,
 }])

 let exceptionThrown = null;
 const response = ResponseMock.create();
 await LoginController.signIn(RequestMock.create({
 body: {
 Authenticate_type: 'FORM',
 email: users[0].email,
 password: 'wrongpw',
 },
 portal: {
 type: 'COACH',
 }
 }), response, (e) => {
 exceptionThrown = e;
 });

 expect(exceptionThrown).toBeInstanceOf(UnauthorizedError);
 expect(exceptionThrown.message).toEqual('It seems like your password is incorrect.');
 });
});
0
ng test –include=’**/sign-in.component.spec.ts’
0

You can do this with response-time plugin but I wanted something a bit more flexible and can easily be used on select routes as middleware, so I made:

import onHeaders from 'on-headers';
import net from "net";
import moment from "moment/moment";
import ApiLog from "../models/RequestLog";

// Slow is 10 seconds
const defaultSlowTime = 10000;

function _getTimeDiff(hrtime) {
 var diff = process.hrtime(hrtime);
 return diff[0] * 1e3 + diff[1] * 1e-6
}

function _ipIsPrivate(ip) {
 if (ip.substring(0,7) === "::ffff:")
 ip = ip.substring(7);

 if (net.isIPv4(ip)) {
 // 10.0.0.0 - 10.255.255.255 || 172.16.0.0 - 172.31.255.255 || 192.168.0.0 - 192.168.255.255
 return /^(10)\.(.*)\.(.*)\.(.*)$/.test(ip) || /^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(ip) || /^(192)\.(168)\.(.*)\.(.*)$/.test(ip)
 }

 // else: ip is IPv6
 const firstWord = ip.split(":").find(el => !!el); //get first not empty word

 // The original IPv6 Site Local addresses (fec0::/10) are deprecated. Range: fec0 - feff
 if (/^fe[c-f][0-f]$/.test(firstWord))
 return true;

 // These days Unique Local Addresses (ULA) are used in place of Site Local.
 // Range: fc00 - fcff
 else if (/^fc[0-f]{2}$/.test(firstWord))
 return true;

 // Range: fd00 - fcff
 else if (/^fd[0-f]{2}$/.test(firstWord))
 return true;

 // Link local addresses (prefixed with fe80) are not routable
 else if (firstWord === "fe80")
 return true;

 // Discard Prefix
 else if (firstWord === "100")
 return true;

 // Any other IP address is not Unique Local Address (ULA)
 return false;
}

export default class {
 static log(onlySlow ) {
 const slowTime = onlySlow === true ? defaultSlowTime : (onlySlow === false ? 0 : onlySlow);

 return (req, res, next) => {
 onHeaders(res, function onHeaders() {
 const {
 log: {
 userId = null,
 } = {},
 ip,
 originalUrl,
 startedAt,
 hrtime,
 } = req;

 const time = _getTimeDiff(hrtime);
 // TODO do we want to add configuration to omit private IPs such as dev computers?
 const isPrivateIp = _ipIsPrivate(ip);

 if (slowTime < time) {
 // async so we don't stop request
 ApiLog.createLog({
 userId,
 startedAt: startedAt.toISOString(),
 endedAt: moment().toISOString(),
 ip: ip,
 url: originalUrl,
 duration: time,
 });
 }
 });

 next();
 }
 }
}

And added 
 app.use((req, res, next) => {
 // This is our own personal version of response-time that we can attach to specific routes
 req.hrtime = process.hrtime();
 req.startedAt = moment();
 });

in my base expressjs route file, now I can easily log certain routes by doing:

router.get('/v2/events/all/:year/:month',
 RequestLogMiddleware.log(true),
 celebrate(calendarEventSchema.retrieveAllEventsByMonth),
 CalendarEventController.retrieveAllEventsByMonth
);
0
Sammaye's Blog
Blog at WordPress.com.
Loading Comments...
Design a site like this with WordPress.com
Get started