Skip to content

Commit

Permalink
feat: netrc credentials logic
Browse files Browse the repository at this point in the history
  • Loading branch information
alvarosabu committed Sep 23, 2024
1 parent 55f33fa commit 81047b7
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 11 deletions.
6 changes: 6 additions & 0 deletions __mocks__/fs.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// we can also use `import`, but then
// every export should be explicitly defined

const { fs } = require('memfs')

module.exports = fs
6 changes: 6 additions & 0 deletions __mocks__/fs/promises.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// we can also use `import`, but then
// every export should be explicitly defined

const { fs } = require('memfs')

module.exports = fs.promises
4 changes: 4 additions & 0 deletions __mocks__/test.netrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
machine api.storyblok.com
login [email protected]
password my_access_token
region eu
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"@types/inquirer": "^9.0.7",
"@types/node": "^22.5.4",
"eslint": "^9.10.0",
"memfs": "^4.11.2",
"pathe": "^1.1.2",
"typescript": "^5.6.2",
"unbuild": "^2.0.0",
Expand Down
74 changes: 74 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 17 additions & 2 deletions src/commands/login/actions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
export const loginWithToken = () => {
console.log('Login')
import { regions } from '../../constants'
import { addNetrcEntry, getNetrcCredentials } from '../../creds'

export const loginWithToken = async () => {
try {
await addNetrcEntry({
machineName: 'api.storyblok.com',
login: '[email protected]',
password: 'my_access_token',
region: regions.EU,
})
const file = await getNetrcCredentials()
console.log(file)
}
catch (error) {
console.error('Error reading or parsing .netrc file:', error)
}
}

export const loginWithEmailAndPassword = () => {
Expand Down
15 changes: 8 additions & 7 deletions src/commands/login/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,13 @@ export const loginCommand = program
console.log(formatHeader(chalk.bgHex('#8556D3').bold.white(` ${commands.LOGIN} `)))

const { strategy } = await inquirer.prompt(loginStrategy)

Check failure on line 47 in src/commands/login/index.ts

View workflow job for this annotation

GitHub Actions / build (20)

No overload matches this call.
console.log(strategy)
}
try {
loginWithToken()
}
catch (error) {
handleError(error as Error)
try {
if (strategy === 'login-with-token') {
loginWithToken()
}
}
catch (error) {
handleError(error as Error)
}
}
})
166 changes: 166 additions & 0 deletions src/creds.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { machine } from 'node:os'

Check failure on line 1 in src/creds.test.ts

View workflow job for this annotation

GitHub Actions / Lint (20)

'machine' is defined but never used
import { addNetrcEntry, getNetrcCredentials, getNetrcFilePath } from './creds'
import { fs, vol } from 'memfs'

Check failure on line 3 in src/creds.test.ts

View workflow job for this annotation

GitHub Actions / Lint (20)

'fs' is defined but never used
import { join } from 'pathe'
// tell vitest to use fs mock from __mocks__ folder
// this can be done in a setup file if fs should always be mocked
vi.mock('node:fs')
vi.mock('node:fs/promises')

beforeEach(() => {
// reset the state of in-memory fs
vol.reset()
})

describe('creds', async () => {
describe('getNetrcFilePath', async () => {
const originalPlatform = process.platform
const originalEnv = { ...process.env }
const originalCwd = process.cwd

beforeEach(() => {
process.env = { ...originalEnv }
})

afterEach(() => {
// Restore the original platform after each test
Object.defineProperty(process, 'platform', {
value: originalPlatform,
})
// Restore process.cwd()
process.cwd = originalCwd
})

it('should return the correct path on Unix-like systems when HOME is set', () => {
// Mock the platform to be Unix-like
Object.defineProperty(process, 'platform', {
value: 'linux',
})

// Set the HOME environment variable
process.env.HOME = '/home/testuser'

const expectedPath = join('/home/testuser', '.netrc')
const result = getNetrcFilePath()

expect(result).toBe(expectedPath)
})

it('should return the correct path on Windows systems when USERPROFILE is set', () => {
// Mock the platform to be Windows
Object.defineProperty(process, 'platform', {
value: 'win32',
})

// Set the USERPROFILE environment variable
process.env.USERPROFILE = 'C:/Users/TestUser'

const expectedPath = join('C:/Users/TestUser', '.netrc')
const result = getNetrcFilePath()

expect(result).toBe(expectedPath)
})

it('should use process.cwd() when home directory is not set', () => {
// Mock the platform to be Unix-like
Object.defineProperty(process, 'platform', {
value: 'linux',
})

// Remove HOME and USERPROFILE
delete process.env.HOME
delete process.env.USERPROFILE

// Mock process.cwd()
process.cwd = vi.fn().mockReturnValue('/current/working/directory')

const expectedPath = join('/current/working/directory', '.netrc')
const result = getNetrcFilePath()

expect(result).toBe(expectedPath)
})

it('should use process.cwd() when HOME is empty', () => {
// Mock the platform to be Unix-like
Object.defineProperty(process, 'platform', {
value: 'linux',
})

// Set HOME to an empty string
process.env.HOME = ''

// Mock process.cwd()
process.cwd = vi.fn().mockReturnValue('/current/working/directory')

const expectedPath = join('/current/working/directory', '.netrc')
const result = getNetrcFilePath()

expect(result).toBe(expectedPath)
})

it('should handle Windows platform when USERPROFILE is not set', () => {
// Mock the platform to be Windows
Object.defineProperty(process, 'platform', {
value: 'win32',
})

// Remove USERPROFILE
delete process.env.USERPROFILE

// Mock process.cwd()
process.cwd = vi.fn().mockReturnValue('C:/Current/Directory')

const expectedPath = join('C:/Current/Directory', '.netrc')
const result = getNetrcFilePath()

expect(result).toBe(expectedPath)
})
})

describe('getNetrcCredentials', () => {
it('should return empty object if .netrc file does not exist', async () => {
const creds = await getNetrcCredentials()
expect(creds).toEqual({})
})
it('should return the parsed content of .netrc file', async () => {
vol.fromJSON({
'test/.netrc': `machine api.storyblok.com
login [email protected]
password my_access_token
region eu`,
}, '/temp')

const credentials = await getNetrcCredentials('/temp/test/.netrc')

expect(credentials['api.storyblok.com']).toEqual({
login: '[email protected]',
password: 'my_access_token',
region: 'eu',
})
})
})

describe('addNetrcEntry', () => {
it('should add a new entry to an empty .netrc file', async () => {
vol.fromJSON({
'test/.netrc': '',
}, '/temp')

await addNetrcEntry({
filePath: '/temp/test/.netrc',
machineName: 'api.storyblok.com',
login: '[email protected]',
password: 'my_access_token',
region: 'eu',
})

const content = vol.readFileSync('/temp/test/.netrc', 'utf8')

expect(content).toBe(`machine api.storyblok.com
login [email protected]
password my_access_token
region eu
`)
})
})
})
Loading

0 comments on commit 81047b7

Please sign in to comment.