Skip to content

Commit

Permalink
feat: login twitter service
Browse files Browse the repository at this point in the history
  • Loading branch information
DominMFD committed Sep 10, 2024
1 parent 8136a10 commit 9265426
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 24 deletions.
38 changes: 31 additions & 7 deletions src/features/twitter/controllers/twitter-controller.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { accountRepositoryMock } from '@/shared/test-helpers/mocks/repositories/
import { tokenRepositoryMock } from '@/shared/test-helpers/mocks/repositories/token-repository.mock';

import { AuthorizeTwitterService } from '../services/authorize-twitter-service';
import { LoginTwitterService } from '../services/login-twitter-service';
import type { TwitterService } from '../services/twitter-service';
import { TwitterController } from './twitter-controller';

Expand All @@ -26,7 +27,14 @@ const makeSut = () => {
)
);

const authController = new TwitterController(authorizeTwitterService);
const loginTwitterService = mock<LoginTwitterService>(
new LoginTwitterService()
);

const authController = new TwitterController(
authorizeTwitterService,
loginTwitterService
);

const req = mockDeep<Request>();
const res = {
Expand All @@ -39,6 +47,7 @@ const makeSut = () => {
return {
authController,
authorizeTwitterService,
loginTwitterService,
mockLogger,
next,
req,
Expand Down Expand Up @@ -69,15 +78,30 @@ describe('[Controller] Twitter', () => {
});

describe('login', () => {
it('should be return 401', () => {
const { authController, next, req, res } = makeSut();
it('should be return link', () => {
const { authController, loginTwitterService, next, req, res } = makeSut();

req.headers.authorization = undefined;
req.headers.authorization = 'Bearer token';

const serviceSpy = vi
.spyOn(loginTwitterService, 'execute')
.mockReturnValue('url');
authController.login(req, res, next);

expect(res.status).toHaveBeenCalledWith(401);
expect(res.json).toHaveBeenCalledWith({ message: 'Unauthorized' });
});
expect(serviceSpy).toHaveBeenCalledWith({
authorization: 'Bearer token',
});
expect(res.json).toHaveBeenCalledWith('url');
}),
it('should be return 401', () => {
const { authController, next, req, res } = makeSut();

req.headers.authorization = undefined;

authController.login(req, res, next);

expect(res.status).toHaveBeenCalledWith(401);
expect(res.json).toHaveBeenCalledWith({ message: 'Unauthorized' });
});
});
});
30 changes: 13 additions & 17 deletions src/features/twitter/controllers/twitter-controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import jwt from 'jsonwebtoken';

import type { TokenPayload } from '@/shared/infra/jwt/jwt';
import type { Controller } from '@/shared/protocols/controller';
import type { AsyncRequestHandler } from '@/shared/protocols/handlers';

import { generateAuthURL } from '../helpers/generate-auth-url';
import type { AuthorizeTwitterService } from '../services/authorize-twitter-service';
import type { LoginTwitterService } from '../services/login-twitter-service';

export class TwitterController implements Controller {
callback: AsyncRequestHandler = async (req, res) => {
Expand All @@ -20,20 +17,19 @@ export class TwitterController implements Controller {
};

login: AsyncRequestHandler = (req, res) => {
const authorization = req.headers.authorization;

if (!authorization) {
return res.status(401).json({ message: 'Unauthorized' });
try {
const url = this.loginTwitter.execute({
authorization: req.headers.authorization,
});

return res.json(url);
} catch (err) {
return res.status(401).json({ message: (err as Error).message });
}

const [, token] = authorization.split(' ');

const payload = jwt.verify(token, 'secret_key') as TokenPayload;

const url = generateAuthURL({ id: payload.userId });

return res.json(url);
};

constructor(private readonly authorizeTwitter: AuthorizeTwitterService) {}
constructor(
private readonly authorizeTwitter: AuthorizeTwitterService,
private readonly loginTwitter: LoginTwitterService
) {}
}
34 changes: 34 additions & 0 deletions src/features/twitter/services/login-twitter-service.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import jwt from 'jsonwebtoken';
import type { Mock } from 'vitest';

import { LoginTwitterService } from './login-twitter-service';

vi.mock('jsonwebtoken', () => ({
default: {
verify: vi.fn(() => ({ userId: '123' })),
},
}));

describe('LoginTwitterService', () => {
let sut: LoginTwitterService;
let mockVerify: Mock;
const jsonwebtoken = jwt;

beforeEach(() => {
sut = new LoginTwitterService();
mockVerify = vi.fn(() => ({ userId: `123` }));
jsonwebtoken.verify = mockVerify;
});

it('should return the generated auth URL', () => {
const authorization = 'Bearer token';
const result = sut.execute({ authorization });

expect(mockVerify).toHaveBeenCalledWith('token', 'secret_key');
expect(result).toContain('https://twitter.com/i/oauth2/authorize');
});

it('should throw an error if authorization header is missing', () => {
expect(() => sut.execute({ authorization: '' })).toThrow('Unauthorized');
});
});
25 changes: 25 additions & 0 deletions src/features/twitter/services/login-twitter-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import jwt from 'jsonwebtoken';

import type { TokenPayload } from '@/shared/infra/jwt/jwt';

import { generateAuthURL } from '../helpers/generate-auth-url';

type Input = {
authorization: string | undefined;
};

export class LoginTwitterService {
execute({ authorization }: Input) {
if (!authorization) {
throw new Error('Unauthorized');
}

const [, token] = authorization.split(' ');

const payload = jwt.verify(token, 'secret_key') as TokenPayload;

const url = generateAuthURL({ id: payload.userId });

return url;
}
}

0 comments on commit 9265426

Please sign in to comment.