Category: bingo

Bingo for meetings-nestjs–create meeting api -part 15

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)

NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)


So now it is time to expose our objects as HTTP API. We have decided to go with nest.js because has support for TypeScript. The documentation to install is pretty obvious at https://docs.nestjs.com/ .

Now we must code the endpoint to create a meeting. As per the nest.js documentation , we should create a module ,a controller and a service to redirect calls . The problem is that , to create a meeting, we should transmit 2 parameters: userName and meetingName. To do this via  HTTP, we must create a new class with this 2 parameters :


export class CreateMeeting {
  readonly userName: string;
  readonly meetingName: string;
}

Also we want to add Swagger as show at https://docs.nestjs.com/recipes/swagger .  And for this, according to the documentation , we should decorate the class:

import { ApiModelProperty } from '@nestjs/swagger';

export class CreateMeeting {
  @ApiModelProperty()
  readonly userName: string;
  @ApiModelProperty()
  readonly meetingName: string;
}

And now we have a controller with 2 functions – get – to show all meetings – and post-  to create the meeting:

@Controller('meetings')
export class MeetingsController {
    constructor(private meetingsService: MeetingService){}

    @Get()
    index(): Meeting[] {
      return this.meetingsService.meetings;
    }

    @Post()
    async create(@Body() cm: CreateMeeting): Promise<Meeting> {
        console.log(`userName : ${JSON.stringify(cm.userName)}  meetingName: ${JSON.stringify(cm.meetingName)}`);
        return this.meetingsService.create(cm.userName, cm.meetingName);

    }
}

And now we have a fully qualified web api that can create a meeting.  And we have swagger to test functions.

That’s good for a start…

Bingo for meetings–typescript making exe console–part 14

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

Now it is the moment to have our first executable release– console application. First, we want to see if the js obtain by compilation of ts run under node ( it runs under  ts-node index.ts , but it runs under node.js ? ). And we see that , when I run node index.js it gives error “ could not find bingo-meeting-object/MeetingFactory”.

It is clear that, even if yean workspaces works well with node and typescript, the final result, index.js , is not prepared to run under node. Solving the problem is about compiling typescript with project references  . The documentation is at https://www.typescriptlang.org/docs/handbook/project-references.html#composite  , and a sample project can be found at https://github.com/RyanCavanaugh/learn-a

It is composed by

1. having a tsconfig.json with

{

“compilerOptions”: {

“target”: “es5”,

“module”: “commonjs”,

“declaration”: true,

“declarationMap”: true,

“sourceMap”: true,

“strict”: false,

“composite”: true,

“esModuleInterop”: true

}

}

and extending in tsconfig.json

“extends”: “../tsconfig.settings.json”,

2. Compiling the projects with

tsc -b .

3. Creating index.ts with all classes exports

export * from “./MeetingsFactory”;

export * from “./Meeting”;

export * from “./Cards”;

export * from “./Participant”;

And now we can execute index.js with node index.jsinto

That means also the referenced projects are compiled into node_modules. That means that will not run without it.

 

We want now to create a console executable for the console project. That means something that compiles everything into an exe  – no matter windows , linux , or macos

We use for that https://www.npmjs.com/package/pkg

In the package.json of the console project we put

“build” : “pkg dist/index.js -c package.json”
In the root package.json we put
“scripts”: {
“test”: “cd bingo-meeting-objects-test && yarn test”,
“runConsole”: “cd bingo-meeting-console && yarn start”,
“buildConsole”:”yarn build && cd bingo-meeting-console && yarn build”,
“build”:”tsc -b .”
}
and yarn buildConsole will build than pkg into windows + linux + macos ( no matter that the windows has 108 MB 😉 )
Also we create a build_console.bat batch and docker_build_console.txt in order to automatically build the console
The docker is simple
FROM node:8
WORKDIR /app
COPY . ./
RUN yarn
RUN yarn buildConsole
CMD tail -f /dev/null
The batch just runs the docker and copy files
docker build .. -f docker_build_console.txt -t bingo_build_console
docker run -d –rm –name bingo_build_console_container bingo_build_console
docker cp bingo_build_console_container:/app/bingo-meeting-console/bingo-meeting-console-win.exe .
docker cp bingo_build_console_container:/app/bingo-meeting-console/bingo-meeting-console-linux .
docker cp bingo_build_console_container:/app/bingo-meeting-console/bingo-meeting-console-macos .
docker container kill bingo_build_console_container
After this, we put into the yaml file to be copied as artifacts

Bingo for meetings–intermezzo – improving application–part 13

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

After done with CLI, now it is time to improve a little bit the application.

1. should be more cards

For this I have replaced

 let c=new Cards();
 c.Name="Who just joined?";
 c.Id = i++;
 ret.push(c);

 c=new Cards();
 c.Name="Can you email that to everyone ?";
 c.Id = i++;
 ret.push(c);

with a local function

let i=1;
const ret=[];
let addCard = (name:string)=>{
    let c=new Cards();
    c.Name=name;
    c.Id = i++;
    ret.push(c);

}

addCard("Who just joined?");
addCard("Can you email that to everyone ?");
addCard("..., are you there ?");
addCard("Can you hear me?");
addCard("I'm sorry, I was on mute");
addCard("I'm sorry, connection issues");
addCard("Hello ? Hello ?");
addCard("Can we take this offline ?");
addCard("Can everyone see my screen ?");
addCard("No, still loading");
addCard("Sorry, I have to go to another call");

As a consequence, the pageSize for displayin via Inquirer.js should be make larger to display all items

2 . The cards should be sorted alphabetically ( easy: apply sort)

3. show cards checked  by bingo when displaying again

4. add question to end meeting ( use null for card and a message ‘End meeting”)( should this be moved into a Display Layer?)

5.
show percentage at final ( calculate and add 2 tests for this )

6.show table with cards situation after meeting ( mapping the cards to columns ). Also, putting into evidence the ones checked by sorting after checked and then after name( should this be moved into a Display Layer?)

7.show current user name instead of asking for name( use username)

All those modifications, no matter how small they are, implies time to solve.And finding correct packages that will solve your problem.

Bingo for meetings- Adding a CLI application–part 12

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

The easy way to test an application is to make a simple command line version . You can find the code at https://github.com/alexandru360/PresentationBingoCards/blob/master/bingo-meeting-console/index.ts

You can run also with Docker by running https://github.com/alexandru360/PresentationBingoCards/blob/master/dockerize/bingo_cli.bat

What were the steps ?

1. Install the @types/node, ts-node , typescript and others – see

https://github.com/alexandru360/PresentationBingoCards/blob/master/bingo-meeting-console/package.json

2. Create an index.ts with the required CLI ( hint: prompt, figlet, chalk , username , inquirer,console.table are good to have) – read https://dev.to/hugodias/building-your-first-interactive-node-js-cli-1g2c

3. Created an async main() and calling with

(async () => {
  try {
      await main();
        } catch (e) {
      console.log(JSON.stringify(e));
  }
})();

 

4. Modify package.json  to have

“main”: “dist/index.js”,

“types”: “dist/index.d.ts”,

“scripts”: {

“start”: “ts-node index.ts”,

“build”: “tsc”,

“compile”: “tsc”

}

5. Put in the root package

“scripts”: {

“test”: “cd bingo-meeting-objects-test && yarn test”,

“runConsole”: “cd bingo-meeting-console && yarn start”

}

and then run yarn runConsole

I can say that the C# console experience is better 😉

You can download the source code from https://github.com/alexandru360/PresentationBingoCards/releases/tag/CLI

Bingo for meetings- yarn workspaces–part 11

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

The problem that we see is how to have the same objects configured for backend and for frontend( e.g. a Meeting is used on the backend on the WebAPI  to read from database and in the frontend to display)

In C# , there is the concept of dll / assembly that is common. In here we have the concept of yarn Workspaces : https://yarnpkg.com/lang/en/docs/cli/workspace/

What we want to achieve first is that the tests will be in a separate workspace bingo-meeting-objects-test , referencing the bingo-meeting-objects workspace

So, in order to do this, we have to modify :

  1. yarn workspaces  to understand the structure and run tests
  2. bingo-meeting-objects   to expose the result
  3. bingo-meeting-objects–test   to import bingo-meeting-objects  
  4. (depending on the projects) Other references  : Modify docker bat file to consider the new structure

Let’s detail:

For yarn workspaces  to understand the structure and run tests

So , first, we move the tests in a separate folder, bingo-meeting-objects–test   , and we run nom init and add dependencies( jest, others)

Second, we add the yarn workspace package.json in the root with the following content:

{
     “private”: true,
     “workspaces”: [“bingo-cards-api”, “bingo-meeting-objects”]
   }
     “workspaces”: [“bingo-cards-api”, “bingo-meeting-objects”, “bingo-meeting-objects-test”],
     “scripts”: {
       “test”: “cd bingo-meeting-objects-test && yarn test”
     }
}

In this manner, we can run yarn test from the root ( do not run yet!)


For bingo-meeting-objects   to expose the result

In the tsconfig.json we put those lines:

“sourceMap”: true,

“declaration”: true,

In the package.json we modify to understand the declaration

“main”: “dist/index.js”,
   “types”: “dist/index.d.ts”,

“scripts”: {
     “build”: “tsc”,
     “compile”: “tsc”,
     “test”: “jest”
   },

For bingo-meeting-objects–test   to import bingo-meeting-objects  

We add jest and others. Also, we added dependency of bingo-meeting-objects:

“dependencies”: {
     “bingo-meeting-objects”: “^1.0.0”,
  

Also, we need to modify the import of the test. Instead of the following line, when test was under subfolder test in the bingo-meeting-objects folder

import  MeetingsFactory from ‘../MeetingsFactory’;

we put

import  MeetingsFactory from “bingo-meeting-objects/MeetingsFactory”;

Now running yarn test in the root folder runs the test sucessfully

For (depending on the projects) Other references  : Modify docker bat file to consider the new structure

We had the batch file that uses docker that was running the tests and then copy the results.

For start, we move the .dockerignore from the project to the root ( to not put node_modules)

Also, we modify how we copy the files to docker

For this, we modify this line

docker cp bingo_ci_test_container:/app/coverage/cobertura-coverage.xml .   

into this line

docker cp bingo_ci_test_container:/app/bingo-meeting-objects-test/coverage/cobertura-coverage.xml .

to take into consideration the new structure

And that will be all! ( code source at https://github.com/alexandru360/PresentationBingoCards/releases/tag/yarnworkspaces )

Bingo for meetings- azure integrations–part 10

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

Now it is about Continuous Integrations. We want tests to be run each time we push something to GitHub.  For this we could use Azure DevOps. It is free for GitHub public repositories . We want to configure an azure pipeline to automatically run tests  that we have in Docker.

So the pipeline will just have to explicit gather the test results ( tests + code coverage ) in order to display in the Azure Pipeline and in the project. Azure DevOps wants the test coverage in JaCoCo or Cobertura . Jest has Istanbul as default test coverage, and Istanbul has Cobertura report. So we modify the jest.config.js to support cobertura

module.exports = {

preset: ‘ts-jest’,

transform: {

‘^.+\\.tsx?$’: ‘ts-jest’,

},

testEnvironment: ‘node’,

collectCoverage: true,

coverageReporters : [“json”, “lcov”, “text”, “clover”,”cobertura”]

};

And to copy when docker building the tests to the local path

docker build ../Src -f docker_ci_test.txt -t bingo_ci_test

docker run -d –rm –name bingo_ci_test_container bingo_ci_test

docker cp bingo_ci_test_container:/app/jest-stare .

docker cp bingo_ci_test_container:/app/junit.xml .

docker cp bingo_ci_test_container:/app/coverage/cobertura-coverage.xml .   

docker container kill bingo_ci_test_container

And then copy to the AzureDevOps test system

#https://docs.microsoft.com/en-us/azure/devops/pipelines/build/options?view=vsts&tabs=yaml

variables:

year: $(Date:yyyy)

month: $(Date:MM)

day: $(Date:dd)

uk: $(Date:yyyyMMdd)

messagePush: $(Build.SourceVersionMessage)

name: $(TeamProject)_$(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r)

jobs:

– job: FullTestOnLinux

pool:

vmImage: ‘ubuntu-16.04’

steps:

– checkout: self #skip checking out the default repository resource

clean: true

– script: |

cd dockerize

ls -l

chmod 777 ./ci_test.bat

./ci_test.bat

docker image ls

docker container ls

cp -r -v ./jest-stare $(Build.ArtifactStagingDirectory)/jest-stare/

cp ./junit.xml $(Build.ArtifactStagingDirectory)/junit.xml

cp ./cobertura-coverage.xml $(Build.ArtifactStagingDirectory)/cobertura-coverage.xml

displayName: test DDD

– task: PublishBuildArtifacts@1

inputs:

artifactName: Tests

displayName: ‘Publish Artifact: drop’

– task: PublishTestResults@2

inputs:

testRunner: JUnit

testResultsFiles: ‘$(Build.ArtifactStagingDirectory)/junit.xml’

– task: PublishCodeCoverageResults@1

inputs:

codeCoverageTool: ‘cobertura’

summaryFileLocation: ‘$(Build.ArtifactStagingDirectory)/cobertura-coverage.xml’

You can see the tests and the test coverage at https://dev.azure.com/ignatandrei0674/BingoAzureDevOps/_build/results?buildId=953&view=ms.vss-test-web.build-test-results-tab

Bingo for meetings- dockerize tests–part 9

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

We have now full DDD and tests that should be run for the objects. However, we need a way to automatically have the tests run . The

easy way is to dockerize the tests – run in a container, grab the results, display somewhere.

First we should have the tests display in a nice form some data.  For this, jest have the “reporters” features – but no documentation . So I try to find and https://github.com/dkelosky/jest-stare .

 

So what are the steps ?

  1. Create docker from node
  2. Copy sources ( add a .dockerignore to not copy node_modules)
  3. Install dependencies
  4. Run test
  5. run image  into container and grab the tests results

 

The docker file , named docker_ci_test.txt , has the following content

FROM node:8
WORKDIR /app
COPY . ./
RUN yarn
RUN yarn test –reporters default jest-stare
CMD tail -f /dev/null

The bat that runs the image and grab results from the container

docker build ../src -f docker_ci_test.txt -t bingo_ci_test
docker run -d –rm –name bingo_ci_test_container bingo_ci_test
docker cp bingo_ci_test_container:/app/jest-stare .
docker container kill bingo_ci_test_container

Feel free to download the project from https://github.com/alexandru360/PresentationBingoCards/  and run the ci_test.bat file from dockerize folder.

Bingo for meetings–hide results–part 8

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)

NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)


The last requirement says

EndMeeting:
The score of how many cards/ what cards were checked will be available 1 hour and 5 minutes

 

We have already done something similar when the meeting was considered obsolete – we decided to not throw error, but use

Result< T, Error> . So I supposed to be easy …. However, it was not. Because we are modifying the results of the functions, we are supposed to modify also the tests….

First , then modification of the code. Because of the refactoring, the AllUnchecked function calls   TotalNumberOfCardsChecked.

 

public TotalNumberOfCardsChecked():number{
        return this.Cards.filter(it=>it.IsChecked()).length ;
    }
    public AllUnchecked(): boolean{
        return (this.TotalNumberOfCardsChecked() === 0);
    }

 

Now, because we cannot TotalNumberOfCardsChecked returns  Result<number, Error> , the function  AllUnchecked must be re-written :

 

     public TotalNumberOfCardsChecked():Result<number, Error>{
        if(this.CanSeeScore()){
            return ok(this.Cards.filter(it=>it.IsChecked()).length );
        }
        else{
            //TODO :Make a proper error
            return err(new Error(`cannot see score for ${this.Id}`));
        }
    }
    public AllUnchecked(): Result< boolean, Error>{
        
        var res=this.TotalNumberOfCardsChecked();
        console.log(" all unchecked" + res.isOk());
        return res.andThen(it=> ok(it===0));
        // const ret= res.match(
        //     (v)=>{return ok(v==0)},
        //     (error)=>{ return error}
        // );
        // return ret;
    }

 

Second , the test will fail. Take “card should be checked” – original code was pretty easy

 

import  MeetingsFactory from '../MeetingsFactory';
import Meeting from '../meeting';
describe('Check  card basic', () => {
    it('card should be checked', () => {
        const mf=new MeetingsFactory();
        const m1=mf.CreateMeeting("andrei","first meeting");
        console.log(m1.Cards.length);
        
        expect(m1.AllUnchecked()).toBe(true);
        m1.CheckCard(m1.Cards[0], m1.Participants[0]);    
        expect(m1.AllUnchecked()).toBe(false);
        expect(m1.IsCardCheckedByParticipant(m1.Cards[0], m1.Participants[0])).toBe(true);
        
        
  
      })
    
  })
  

 

Now it is

 

it('card should be checked', () => {
        const mf=new MeetingsFactory();
        const m1=mf.CreateMeeting("andrei","first meeting");
        console.log(m1.Cards.length);
        let res=m1.AllUnchecked();
        expect(res.isOk()).toBe(true);
        let result= false;
        res.map(t=> result =t);
        expect(result).toBe(true);
        let res1 = m1.CheckCardByParticipant(m1.Cards[0], m1.Participants[0]);
        expect(res1.isOk()).toBe(true);
        res=m1.AllUnchecked();
        expect(res.isOk()).toBe(true);
        result= false;
        res.map(t=> result =t);
        expect(result).toBe(false);            
        expect(m1.IsCardCheckedByParticipant(m1.Cards[0], m1.Participants[0]).isOk()).toBe(true);  
      })

 

Bingo for meetings–obsolete–re-reading requirements- part 7

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)
NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)

Last time we have to implement the requirement:

Meeting Obsolete:
The meeting is available for 35 minutes. After that, meeting is not available anymore.

We did so for the 35 minutes. But we did not for the last sentence – meeting is not available anymore. There are 2 points here: of design ( do not retrieve meeting ) and enforcing that no participant can write to the meeting.

So we modify the functions   AddParticipant, CheckCardByParticipant  to raise an exception if the meeting is obsolete .( As a side effect of identifying the functions that make actions in the opposite of functions that just reports, I think about structuring code in CQRS form )

Now for TypeScript we have 2 options :

  1. Construct a class that inherits from Error
  2. Modify the response type of those function to a combined type of result and error

For the first one , we should take care of TypeScript syntax of creating errors: See https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html  and https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work

class CustomError extends Error {

constructor(message?: string) {

super(message);

// ‘Error’ breaks prototype chain here

Object.setPrototypeOf(this, new.target.prototype); // restore prototype chain

}

}

For the second , we could do ourselves or use a npm package such as https://github.com/gDelgado14/neverthrow

I decide to go to the second. I have too many times use the first thing in C# – it is time to have something new. Anyway, this is the code now

    import { ok, err, Result } from 'neverthrow';
//code
    public CheckCardByParticipant(c: Cards , p:Participant): Result<Meeting,Error>{
        //TODO: verify participant is added first or add
        //TODO: verify card is added first
        if(this.IsObsolete()){
            return err(new Error(`cannot check card to the obsolete meeting ${this.Id}`));
        }
        c.CheckMe(p);
        return ok(this);
    }
//code
    public AddParticipant(p:Participant ): Result<number,Error>{
        if(this.IsObsolete()){
            return err(new Error(`cannot add participant to the obsolete meeting ${this.Id}`));
        }

        this.Participants.push(p);
        return ok(this.Participants.length);
    }

and those are the tests

        const mf=new MeetingsFactory();

        const m1=mf.CreateMeeting("andrei","first meeting");
        const now = Date.now();
        const spy = jest.spyOn(Date,'now');
        spy.mockImplementation(()=>{
          console.log('calling DateTime Now');
          return now + 36 * 60* 1000;
        } );
        expect(m1.IsObsolete()).toBe(true);
        const p=new Participant();
        p.Id=70;
        p.Name ="alexandru";
        const res= m1.AddParticipant(p);
        expect(res.isOk()).toBe(false);
        spy.mockRestore();

Bingo for meetings–obsolete–part 6

Bingo is a small project, written in TypeScript , and developed with Alexandru Badita in launch break (one hour - more or less). You can find sources at https://github.com/alexandru360/PresentationBingoCards/ . Those are my blog posts for Bingo : ( scroll below for the post)

NrLink
1Create meeting
2Create Tests
3Finalize Create meeting
4Sharing meeting
5Keep Score
6Add obsolete
7Finalizing obsolete
8End meeting
9Dockerize tests
10Azure CI tests
11Yarn workspaces
12CLI
13Intermezzo - CLI improvements
14typescript compile run with node
15NestJS ,swagger and create a meeting
16Finalizing API
17Intermezzo - jest vs jasmine error
18Refactor WebAPI and test service
19Heroku Deploy NestJs
20Angular
21Deploy Angular to GitHub

(Now the actual blog post for Bingo Meetings project)


The requirement says:

Meeting Obsolete:
The meeting is available for 35 minutes. After that, meeting is not available anymore.

How we can implement this ? Several solutions:

  1. Make the meeting know about this ( and avoiding https://martinfowler.com/bliki/AnemicDomainModel.html )
  2. Make a decorator class for this https://en.wikipedia.org/wiki/Decorator_pattern
  3. Make a mixin in TypeScript https://www.typescriptlang.org/docs/handbook/mixins.html

 

I decide to KISS (https://en.wikipedia.org/wiki/KISS_principle ) and take the first point. We implement an Obsolete function. Remains who is responsible of the site to call it.

Now comes other question: When the meeting starts  ?I consider ( for the sake of easy path) that a meeting starts when it is created . So the code is like this

export default class Meeting{

    constructor(){
        this.Participants = [];        
        this.Cards = [];
        this.startedMeeting = Date.now();
    }
    public static  MaxTime=35 * 60 * 1000;
    // other code here
    public IsObsolete(): boolean{
        return (this.PassedTimeFromStart() > Meeting.MaxTime); //35 minutes
    }
    public PassedTimeFromStart():number{
        let dtNow =  Date.now();
        console.log(dtNow);
        return (dtNow - this.startedMeeting );
    }

 

 

Now the problem arises when testing code. It is very easy to say that a meeting is not obsolete when just created. However , I do not want to wait for 35minutes + 1 second in order for a meeting to become obsolete and the test to be successful( see
//TODO: wait 35 minutes + 1 second

below).


import  MeetingsFactory from '../MeetingsFactory';
import Meeting from '../meeting';
describe('Meeting Obsolete', () => {
    it('meeting should not be obsolete after creation', () => {
        const mf=new MeetingsFactory();
        const m1=mf.CreateMeeting("andrei","first meeting");
        expect(m1.IsObsolete()).toBe(false);
        
        
  
      })
      it('meeting should  be obsolete after 35 minutes', () => {
        const mf=new MeetingsFactory();

        const m1=mf.CreateMeeting("andrei","first meeting");
        //TODO: wait 35 minutes + 1 second
        expect(m1.IsObsolete()).toBe(true);
        
  
      })
  })
  

One possible resolution is https://ayende.com/blog/3408/dealing-with-time-in-tests . However, the problem is so common that here must be include in the test framework ( in this case, jest). And , indeed, it is a way: spyon. So the code is modified accordingly :

it('meeting should  be obsolete after 35 minutes', () => {
        const mf=new MeetingsFactory();

        const m1=mf.CreateMeeting("andrei","first meeting");
        const now = Date.now();
        const spy = jest.spyOn(Date,'now');
        spy.mockImplementation(()=>{
          console.log('calling DateTime Now');
          return now + 36 * 60* 1000;
        } );
        expect(m1.IsObsolete()).toBe(true);
        //spy.mockClear();        
        spy.mockRestore();
        expect(m1.IsObsolete()).toBe(false);
        
  
      })

The testing code now assumes that he knows inner working of the code – and the test will fail if we modify the call of Date.now().
So I think that https://ayende.com/blog/3408/dealing-with-time-in-tests is far superior.

Andrei Ignat weekly software news(mostly .NET)

* indicates required

Please select all the ways you would like to hear from me:

You can unsubscribe at any time by clicking the link in the footer of our emails. For information about our privacy practices, please visit our website.

We use Mailchimp as our marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp's privacy practices here.