Skip to content

Commit

Permalink
create logic and spec of createProduct in cor/logic b00tc4mp#101
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasOrtsBaguena committed Aug 15, 2024
1 parent 86c85d9 commit 09d87d0
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 10 deletions.
18 changes: 18 additions & 0 deletions staff/lucas-orts/project/app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>

<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
</head>

<body>
<div id="root"></div>

<script src="./index.jsx" type="module"></script>
</body>

</html>
9 changes: 9 additions & 0 deletions staff/lucas-orts/project/app/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import ReactDOM from 'react-dom/client'
import { BrowserRouter as Router } from 'react-router-dom'

import App from './view/App'

import './index.css'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(<Router><App /></Router>)
Empty file.
1 change: 1 addition & 0 deletions staff/lucas-orts/project/app/logic/isUserLoggedIn.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => !!sessionStorage.token
57 changes: 57 additions & 0 deletions staff/lucas-orts/project/app/view/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState, useEffect } from 'react'
import { Routes, Route, useNavigate, Navigate } from 'react-router-dom'

import Login from './login'
import Register from './register'
import Home from './home'
import Alert from './common/Alert'

import { Context } from './context'

import logic from '../logic'

const App = () => {
const navigate = useNavigate()

const [theme, setTheme] = useState(localStorage.theme)
const [alertMessage, setAlertMessage] = useState(null)

useEffect(() => {
document.documentElement.className = theme
localStorage.theme = theme
}, [theme])

const handleLogin = () => {
navigate('/')
}

const handleRegisterClick = () => {
navigate('/register')
}

const handleRegister = () => {
navigate('/login')
}

const handleLoginClick = () => {
navigate('/login')
}

const handleLogout = () => {
navigate('/login')
}

const handleAlertAccept = () => setAlertMessage(null)

return <Context.Provider value={{ theme, setTheme, alert: setAlertMessage }}>
<Routes>
<Route path="/login"><Login onLogin={handleLogin} onRegisterClick={handleRegisterClick} /></Route>
<Route path="/register"> <Register onRegister={handleRegister} onLoginClick={handleLoginClick} /></Route>
<Route path="/*"><Home onLogout={handleLogout} /></Route>
</Routes>

{alertMessage && <Alert message={alertMessage} onAccept={handleAlertAccept} />}
</Context.Provider>
}

export default App
13 changes: 12 additions & 1 deletion staff/lucas-orts/project/com/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,24 @@ function validateEmail(email) {
if (!EMAIL_REGEX.test(email)) throw new ValidationError(`invalid email`)
}

function validateUrl(url, explain = 'url') {
validateString(url, explain)
if (!url.startsWith('http')) throw new ValidationError(`invalid ${explain}`)
}

function validateNumber(value, explain = 'value') {
if (typeof value !== 'number') throw new ValidationError(`${explain} is not a number`)
}

const validate = {
string: validateString,
password: validatePassword,
name: validateName,
address: validateAddress,
email: validateEmail,
phone: validatePhone
phone: validatePhone,
url: validateUrl,
number: validateNumber
}

export default validate
26 changes: 17 additions & 9 deletions staff/lucas-orts/project/cor/data/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,27 @@ const product = new Schema({
type: {
type: String
},
minprize: {
type: Number,
required: true
},
maxprize: {
type: Number,
required: true
},
image: {
type: String,
required: true
},
location: {
type: [Number],
validate: {
validator: function (arr) {
return arr.length === 2
},
message: 'The array must contain exactly two numbers.'
}
},
// location: {
// type: [Number],
// validate: {
// validator: function (arr) {
// return arr.length === 2
// },
// message: 'The array must contain exactly two numbers.'
// }
// },
enabled: {
type: Boolean,
required: true,
Expand Down
30 changes: 30 additions & 0 deletions staff/lucas-orts/project/cor/logic/createProduct.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { User, Product } from '../data/models.js'

import { validate, errors } from 'com'

const { NotFoundError, SystemError } = errors

export default (userId, name, type, minprize, maxprize, image) => {
validate.string(userId, 'userId')
validate.string(name, 'name')
validate.string(type, 'type')
validate.number(minprize, 'minprize')
validate.number(maxprize, 'maxprize')
validate.url(image, 'image')
return User.findById(userId).lean()
.catch(error => { throw new SystemError(error.message) })
.then(user => {
if (!user) throw new NotFoundError('user not found')

return Product.create({
farmer: userId,
name,
type,
minprize,
maxprize,
image
})
.catch(error => { throw new SystemError(error.message) })
})
.then(() => { })
}
141 changes: 141 additions & 0 deletions staff/lucas-orts/project/cor/logic/createProduct.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import 'dotenv/config'
import mongoose, { Types } from 'mongoose'
import { expect } from 'chai'

const { ObjectId } = Types

import createProduct from './createProduct.js'
import { User, Product } from '../data/models.js'

import errors from '../../com/errors.js'
const { ValidationError, NotFoundError } = errors

describe('createProduct', () => {
before(() => mongoose.connect(process.env.MONGODB_URI))

beforeEach(() => Promise.all([User.deleteMany(), Product.deleteMany()]))

it('succeeds on new product', () =>
User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero', phone: '966234731', address: 'calle Tertulia 3, Cuenca', password: '123123123' })
.then(user =>
createProduct(user.id, 'lemon', '', 5.3, 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
.then(() => Product.findOne({ farmer: user.id })
.then(product => {
expect(product.farmer.toString()).to.equal(user.id)
expect(product.name).to.equal('lemon')
expect(product.type).to.equal('')
expect(product.minprize).to.equal(5.3)
expect(product.maxprize).to.equal(6.1)
expect(product.image).to.equal('https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
})
)
)
)


it('fails on non-existing user', () => {
let _error

return createProduct(new ObjectId().toString(), 'lemon', '', 5.3, 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
.catch(error => _error = error)
.finally(() => {
expect(_error).to.be.instanceOf(NotFoundError)
expect(_error.message).to.equal('user not found')
})
})

it('fails on non-string userId', () => {
let error

try {
createProduct(123, 'lemon', '', 5.3, 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('userId is not a string')
}
})

it('fails on non-string name', () => {
let error

try {
createProduct(new ObjectId().toString(), 123, '', 5.3, 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('name is not a string')
}
})

it('fails on non-string type', () => {
let error

try {
createProduct(new ObjectId().toString(), 'lemon', 123, 5.3, 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('type is not a string')
}
})

it('fails on non-number minprize', () => {
let error

try {
createProduct(new ObjectId().toString(), 'lemon', '', '5.3', 6.1, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('minprize is not a number')
}
})

it('fails on non-number maxprize', () => {
let error

try {
createProduct(new ObjectId().toString(), 'lemon', '', 5.3, '6.1', 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('maxprize is not a number')
}
})

it('fails on non-string image', () => {
let error

try {
createProduct(new ObjectId().toString(), 'lemon', '', 5.3, 6.1, 321)
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('image is not a string')
}
})

it('fails on invalid image', () => {
let error

try {
createProduct(new ObjectId().toString(), 'lemon', '', 5.3, 6.1, 'htpp:')
} catch (_error) {
error = _error
} finally {
expect(error).to.be.instanceOf(ValidationError)
expect(error.message).to.equal('invalid image')
}
})

afterEach(() => Promise.all([User.deleteMany, Product.deleteMany()]))

after(() => mongoose.disconnect())
})

0 comments on commit 09d87d0

Please sign in to comment.