init
This commit is contained in:
commit
e6b7c987d8
115
.gitignore
vendored
Normal file
115
.gitignore
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
.env.test
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
node_modules
|
12
.idea/coding-challenge.iml
Normal file
12
.idea/coding-challenge.iml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/coding-challenge.iml" filepath="$PROJECT_DIR$/.idea/coding-challenge.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
104
.idea/workspace.xml
Normal file
104
.idea/workspace.xml
Normal file
@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="8f59754f-8b0b-4496-b8f6-b19be929e084" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/tests/buyer.test.js" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/Buyer.js" beforeDir="false" afterPath="$PROJECT_DIR$/Buyer.js" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="JavaScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="1fwjsJ2NtOTdTtAo0YfeKXnfbHz" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="true" />
|
||||
<property name="dart.analysis.tool.window.visible" value="false" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
|
||||
<property name="nodejs.jest.jest_package" value="$PROJECT_DIR$/node_modules/jest" />
|
||||
<property name="settings.editor.selected.configurable" value="reference.settingsdialog.IDE.editor.colors" />
|
||||
</component>
|
||||
<component name="RunManager" selected="Jest.buyer.test.js">
|
||||
<configuration name="Buyer" type="JavaScriptTestRunnerJest" temporary="true" nameIsGenerated="true">
|
||||
<node-interpreter value="$USER_HOME$/.nvm/versions/node/v12.18.0/bin/node" />
|
||||
<node-options value="" />
|
||||
<jest-package value="$PROJECT_DIR$/node_modules/jest" />
|
||||
<working-dir value="$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<scope-kind value="SUITE" />
|
||||
<test-file value="$PROJECT_DIR$/tests/buyer.test.js" />
|
||||
<test-names>
|
||||
<test-name value="Buyer" />
|
||||
</test-names>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="buyer.test.js" type="JavaScriptTestRunnerJest" temporary="true" nameIsGenerated="true">
|
||||
<node-interpreter value="$USER_HOME$/.nvm/versions/node/v12.18.0/bin/node" />
|
||||
<node-options value="" />
|
||||
<jest-package value="$PROJECT_DIR$/node_modules/jest" />
|
||||
<working-dir value="$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<scope-kind value="TEST_FILE" />
|
||||
<test-file value="$PROJECT_DIR$/tests/buyer.test.js" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<recent_temporary>
|
||||
<list>
|
||||
<item itemvalue="Jest.buyer.test.js" />
|
||||
<item itemvalue="Jest.Buyer" />
|
||||
</list>
|
||||
</recent_temporary>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="8f59754f-8b0b-4496-b8f6-b19be929e084" name="Default Changelist" comment="" />
|
||||
<created>1597136254764</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1597136254764</updated>
|
||||
<workItem from="1597136255857" duration="11000" />
|
||||
<workItem from="1597136289338" duration="7691000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.bottom" timestamp="1597146833789">
|
||||
<screen x="0" y="29" width="2560" height="1411" />
|
||||
</state>
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.bottom/0.29.2560.1411@0.29.2560.1411" timestamp="1597146833789" />
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.center" timestamp="1597146833789">
|
||||
<screen x="0" y="29" width="2560" height="1411" />
|
||||
</state>
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.center/0.29.2560.1411@0.29.2560.1411" timestamp="1597146833789" />
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.left" timestamp="1597146833788">
|
||||
<screen x="0" y="29" width="2560" height="1411" />
|
||||
</state>
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.left/0.29.2560.1411@0.29.2560.1411" timestamp="1597146833788" />
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.right" timestamp="1597146833789">
|
||||
<screen x="0" y="29" width="2560" height="1411" />
|
||||
</state>
|
||||
<state width="2463" height="519" key="GridCell.Tab.0.right/0.29.2560.1411@0.29.2560.1411" timestamp="1597146833789" />
|
||||
</component>
|
||||
<component name="com.intellij.coverage.CoverageDataManagerImpl">
|
||||
<SUITE FILE_PATH="coverage/coding_challenge$buyer_test_js.info" NAME="buyer.test.js Coverage Results" MODIFIED="1597143325899" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="JestJavaScriptTestRunnerCoverage" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" />
|
||||
</component>
|
||||
</project>
|
29
.vscode/launch.json
vendored
Normal file
29
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "main",
|
||||
"program": "${workspaceFolder}/main.js"
|
||||
},
|
||||
|
||||
{
|
||||
"type": "node",
|
||||
"name": "vscode-jest-tests",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--runInBand"
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"console": "integratedTerminal",
|
||||
"internalConsoleOptions": "neverOpen",
|
||||
"disableOptimisticBPs": true,
|
||||
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
||||
}
|
||||
]
|
||||
}
|
83
Buyer.js
Normal file
83
Buyer.js
Normal file
@ -0,0 +1,83 @@
|
||||
class Buyer {
|
||||
constructor(market) {
|
||||
this.market = market;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an order preference table for particular product
|
||||
* @param product
|
||||
*/
|
||||
buildOrderPreference(product) {
|
||||
let sellers = [];
|
||||
|
||||
// build up the initial list
|
||||
this.market.sellers.forEach(seller => {
|
||||
if (seller.inventory.hasOwnProperty(product)) {
|
||||
const price = seller.quote(product);
|
||||
sellers.push({id:seller.id, price:price})
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// sort by best price
|
||||
sellers.sort((a, b) => {
|
||||
return a.price - b.price;
|
||||
})
|
||||
|
||||
return sellers;
|
||||
}
|
||||
/**
|
||||
* This method should get the best price for a given product
|
||||
* across all sellers
|
||||
*/
|
||||
getBestPrice(product) {
|
||||
let lowestPrice = null;
|
||||
this.market.sellers.forEach(seller => {
|
||||
if (seller.inventory.hasOwnProperty(product)) {
|
||||
const price = seller.quote(product);
|
||||
if (lowestPrice === null || lowestPrice > price) lowestPrice = price;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return lowestPrice || 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method should optimise price when filling an order
|
||||
* if the quantity is greater than any single seller can accomodate
|
||||
* then the next cheapest seller should be used.
|
||||
*/
|
||||
fillWithBestPrices(product, quantity) {
|
||||
|
||||
let sellerPreference = this.buildOrderPreference(product);
|
||||
|
||||
console.log(sellerPreference);
|
||||
|
||||
while(sellerPreference.length > 0) {
|
||||
|
||||
let seller = sellerPreference.shift();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
throw Error("Not Implemented");
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This method should optimise for sellers with the largest inventory when filling an order
|
||||
* if the quantity is greater than any single seller can accomodate
|
||||
* then the next largest seller should be used.
|
||||
* if multiple sellers have the same amount of inventory
|
||||
* you should use the cheaper of the two.
|
||||
*/
|
||||
fillWithLargestSellers(product, quantity) {
|
||||
throw Error("Not Implemented");
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Buyer}
|
20
Market.js
Normal file
20
Market.js
Normal file
@ -0,0 +1,20 @@
|
||||
const { Subject, interval } = require('rxjs');
|
||||
|
||||
class Market {
|
||||
constructor(sellers) {
|
||||
this.sellers = sellers;
|
||||
this.observable = new Subject();
|
||||
this.observable.subscribe({
|
||||
next: (v) => this.tick()
|
||||
});
|
||||
interval(5000).subscribe(v => this.observable.next(v));
|
||||
}
|
||||
|
||||
tick(){
|
||||
|
||||
this.sellers.forEach(seller => {
|
||||
seller.tick()
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.Market = Market;
|
70
Seller.js
Normal file
70
Seller.js
Normal file
@ -0,0 +1,70 @@
|
||||
const stream = require('stream');
|
||||
const rand = require('random-seed');
|
||||
|
||||
|
||||
function getExpectedChange(generator) {
|
||||
return generator(100) / 100;
|
||||
}
|
||||
|
||||
function getDeliveries(iProduct, generator) {
|
||||
let fluctuation = getExpectedChange(generator);
|
||||
let newDeliveries = fluctuation * iProduct.startingQuantity;
|
||||
iProduct.quantity += iProduct.quantity + newDeliveries;
|
||||
return iProduct;
|
||||
}
|
||||
|
||||
class Seller {
|
||||
constructor(inventory, id = "Safeway", deliveryWait = 5) {
|
||||
this.inventory = inventory;
|
||||
this.deliveryWait = deliveryWait;
|
||||
this.random_generator = rand(id);
|
||||
this.id = id;
|
||||
for (let [key, value] of Object.entries(inventory)) {
|
||||
value.startingQuantity = value.quantity;
|
||||
value.priceHistory = [value.price];
|
||||
value.stingyness = 0;
|
||||
}
|
||||
}
|
||||
quote(product) {
|
||||
const inventory = this.inventory[product];
|
||||
return inventory.price;
|
||||
}
|
||||
|
||||
calculatePriceChange(product){
|
||||
const inventory = this.inventory[product];
|
||||
const v = 0.1
|
||||
const ec = getExpectedChange(this.random_generator);
|
||||
const alpha = inventory.startingQuantity
|
||||
const beta = inventory.quantity
|
||||
const inv_based_change = Math.log10(beta / alpha) * (-v);
|
||||
const sentimentChange = inv_based_change + ((ec - 0.5)*v)
|
||||
return sentimentChange;
|
||||
}
|
||||
|
||||
sell(product, buyQuantity) {
|
||||
const inventory = this.inventory[product];
|
||||
const boughtQuantity = buyQuantity > inventory.quantity ? inventory.quantity : buyQuantity;
|
||||
const cost = boughtQuantity * this.quote(product);
|
||||
inventory.quantity -= boughtQuantity;
|
||||
inventory.stingyness = 1 - inventory.quantity / inventory.startingQuantity;
|
||||
this.tick();
|
||||
return {boughtQuantity, cost};
|
||||
}
|
||||
|
||||
|
||||
tick() {
|
||||
for (let [product, value] of Object.entries(this.inventory)) {
|
||||
let inventory = value;
|
||||
const isReadyForDelivery = (inventory.priceHistory.length % this.deliveryWait) == 0;
|
||||
if (isReadyForDelivery) {
|
||||
inventory = getDeliveries(inventory, this.random_generator);
|
||||
}
|
||||
let chg = this.calculatePriceChange(product);
|
||||
inventory.price = inventory.price + (inventory.price*chg)
|
||||
inventory.priceHistory.push(inventory.price);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {Seller}
|
27
main.js
Normal file
27
main.js
Normal file
@ -0,0 +1,27 @@
|
||||
const {asda,costco,budgens} = require("./marketplace");
|
||||
const {Market} = require("./Market");
|
||||
const { Buyer } = require("./Buyer");
|
||||
|
||||
|
||||
function main(){
|
||||
const market = new Market([asda,budgens,costco]);
|
||||
let buyer = new Buyer(market);
|
||||
let product = "Apples";
|
||||
let quantity = 10;
|
||||
buyerFunctions(product, quantity, buyer);
|
||||
// observeMarket(market);
|
||||
};
|
||||
|
||||
function buyerFunctions(product, quantity, buyer){
|
||||
console.log(`The best price for ${product} is ${buyer.getBestPrice(product)}`) ;
|
||||
console.log(`To completely fill a order of ${quantity} ${product} costs ${buyer.completelyFill(product,quantity)}`) ;
|
||||
console.log(`To buy as quickly as possible ${quantity} ${product} costs ${buyer.quicklyFill(product,quantity)}`) ;
|
||||
|
||||
}
|
||||
|
||||
function observeMarket(market){
|
||||
market.observable.subscribe( (mkt) => {
|
||||
console.log(`The current price of apples are ${market.sellers[0].inventory["Apples"].price}`)});
|
||||
}
|
||||
|
||||
main();
|
69
marketplace.js
Normal file
69
marketplace.js
Normal file
@ -0,0 +1,69 @@
|
||||
const { Market } = require("./Market");
|
||||
const { Seller } = require("./Seller")
|
||||
|
||||
const asda = new Seller({
|
||||
"Apples":{
|
||||
quantity:100,
|
||||
price:5.25
|
||||
},
|
||||
"Oranges":{
|
||||
quantity:150,
|
||||
price:8.0
|
||||
},
|
||||
"Pears":{
|
||||
quantity:10,
|
||||
price:15.0
|
||||
},
|
||||
"Banannas":{
|
||||
quantity:1000,
|
||||
price:10.0
|
||||
}
|
||||
}, "Asda", 5);
|
||||
|
||||
const budgens = new Seller({
|
||||
"Apples":{
|
||||
quantity:25,
|
||||
price:4.25
|
||||
},
|
||||
"Oranges":{
|
||||
quantity:15,
|
||||
price:6.0
|
||||
},
|
||||
"Grapes":{
|
||||
quantity:10,
|
||||
price:21.0
|
||||
},
|
||||
"Banannas":{
|
||||
quantity:100,
|
||||
price:4.0
|
||||
}
|
||||
}, "Budgens", 1);
|
||||
|
||||
const costco = new Seller({
|
||||
"Apples":{
|
||||
quantity:250,
|
||||
price:6.25
|
||||
},
|
||||
"Oranges":{
|
||||
quantity:300,
|
||||
price:10.0
|
||||
},
|
||||
"Grapes":{
|
||||
quantity:100,
|
||||
price:35.0
|
||||
},
|
||||
"Pears":{
|
||||
quantity:100,
|
||||
price:30.0
|
||||
},
|
||||
"Mangosteen":{
|
||||
quantity:10,
|
||||
price:100.0
|
||||
},
|
||||
"Banannas":{
|
||||
quantity:100,
|
||||
price:8.0
|
||||
}
|
||||
}, "Costco", 10);
|
||||
|
||||
module.exports = {asda,budgens, costco}
|
4669
package-lock.json
generated
Normal file
4669
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
package.json
Normal file
20
package.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "codingtest",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start": "node main.js",
|
||||
"test": "jest"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"jest": "^26.0.1",
|
||||
"expect": "^26.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"random-seed": "^0.3.0",
|
||||
"rxjs": "^6.5.5"
|
||||
}
|
||||
}
|
47
tests/buyer.test.js
Normal file
47
tests/buyer.test.js
Normal file
@ -0,0 +1,47 @@
|
||||
const expect = require("expect")
|
||||
const {asda, costco, budgens} = require("../marketplace");
|
||||
const {Market} = require("../Market");
|
||||
const {Buyer} = require("../Buyer");
|
||||
|
||||
describe("Buyer", function () {
|
||||
var market;
|
||||
beforeEach(() => {
|
||||
market = new Market([asda, budgens, costco]);
|
||||
})
|
||||
|
||||
|
||||
// getBestPrice
|
||||
|
||||
it("should return best price", () => {
|
||||
let buyer = new Buyer(market);
|
||||
|
||||
expect(buyer.getBestPrice('Apples')).toEqual(4.25);
|
||||
});
|
||||
|
||||
it("should return best price that only 2 have", () => {
|
||||
let buyer = new Buyer(market);
|
||||
|
||||
expect(buyer.getBestPrice('Grapes')).toEqual(21);
|
||||
});
|
||||
|
||||
it("should return best price that only 1 has", () => {
|
||||
let buyer = new Buyer(market);
|
||||
|
||||
expect(buyer.getBestPrice('Mangosteen')).toEqual(100.0);
|
||||
});
|
||||
|
||||
it("should return 0 when fruit doesnt exist", () => {
|
||||
let buyer = new Buyer(market);
|
||||
|
||||
expect(buyer.getBestPrice('Kumquat')).toEqual(0);
|
||||
});
|
||||
|
||||
|
||||
it("fill", () => {
|
||||
let buyer = new Buyer(market);
|
||||
|
||||
expect(buyer.fillWithBestPrices('Apples', 50)).toEqual(0);
|
||||
});
|
||||
|
||||
|
||||
});
|
100
tests/seller.test.js
Normal file
100
tests/seller.test.js
Normal file
@ -0,0 +1,100 @@
|
||||
const expect = require("expect")
|
||||
const {Seller} = require("../Seller")
|
||||
|
||||
|
||||
describe("Seller", function(){
|
||||
var sellerInventory;
|
||||
beforeEach(() => {
|
||||
sellerInventory = {
|
||||
"Apples":{
|
||||
quantity:100,
|
||||
price:5.25
|
||||
},
|
||||
"Oranges":{
|
||||
quantity:150,
|
||||
price:8.0
|
||||
},
|
||||
"Pears":{
|
||||
quantity:10,
|
||||
price:15.0
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it("should reduce quantity when i sell", ()=> {
|
||||
let sut = new Seller(sellerInventory)
|
||||
sut.sell("Apples", 25)
|
||||
expect(sut.inventory["Apples"].quantity).toEqual(75)
|
||||
});
|
||||
|
||||
it("should cap at 0 if I sell more than i have", () =>{
|
||||
let sut = new Seller(sellerInventory);
|
||||
sut.sell("Apples", 105);
|
||||
expect(sut.inventory["Apples"].quantity).toEqual(0);
|
||||
});
|
||||
|
||||
it('should be maximally stingy when empty inventory', () => {
|
||||
let sut = new Seller(sellerInventory);
|
||||
sut.sell("Apples", 105);
|
||||
expect(sut.inventory["Apples"].stingyness).toEqual(1);
|
||||
});
|
||||
|
||||
it("should be minimally stingy when full inventory", () => {
|
||||
let sut = new Seller(sellerInventory);
|
||||
expect(sut.inventory["Apples"].stingyness).toEqual(0);
|
||||
});
|
||||
|
||||
it("should be somewhat stingy when half inventory", () => {
|
||||
let sut = new Seller(sellerInventory);
|
||||
sut.sell("Apples", sut.inventory["Apples"].quantity/2);
|
||||
expect(sut.inventory["Apples"].stingyness).toEqual(0.5);
|
||||
});
|
||||
|
||||
it("should quote initial price on first ask", ()=>{
|
||||
let sut = new Seller(sellerInventory);
|
||||
expect(sut.inventory["Oranges"].price).toEqual(8.0);
|
||||
});
|
||||
|
||||
it("should raise prices after seller buys", () =>{
|
||||
let sut = new Seller(sellerInventory, "Kwiksave");
|
||||
sut.sell("Oranges", sut.inventory["Oranges"].quantity/2);
|
||||
expect(sut.inventory["Oranges"].price).toBeGreaterThan(8.0);
|
||||
});
|
||||
|
||||
it("should get deliveries after seller buys once", () =>{
|
||||
const deliveryCadence = 1;
|
||||
let sut = new Seller(sellerInventory,"Asda",deliveryCadence);
|
||||
sut.sell("Oranges", 1);
|
||||
expect(sut.inventory["Oranges"].quantity).toBeGreaterThan(sut.inventory["Oranges"].startingQuantity);
|
||||
});
|
||||
|
||||
it("should be able to set delivery schedule", () => {
|
||||
const deliveryCadence = 3;
|
||||
let sut = new Seller(sellerInventory,"Asda",deliveryCadence);
|
||||
allOranges = sut.inventory["Oranges"].quantity;
|
||||
sut.sell("Oranges", allOranges);
|
||||
expect(sut.inventory["Oranges"].quantity).toEqual(0);
|
||||
sut.tick("Oranges");
|
||||
expect(sut.inventory["Oranges"].quantity).toEqual(0);
|
||||
sut.tick("Oranges");
|
||||
expect(sut.inventory["Oranges"].quantity).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("should return correct receipt when seller sells single unit", () =>{
|
||||
let sut = new Seller(sellerInventory);
|
||||
const buyAmount = 10;
|
||||
let receipt = sut.sell("Oranges", buyAmount);
|
||||
expect(receipt.cost).toEqual(sut.inventory["Oranges"].priceHistory[0] * buyAmount);
|
||||
expect(receipt.boughtQuantity).toEqual(buyAmount);
|
||||
})
|
||||
|
||||
it("should return correct receipt when seller sells all stock", () =>{
|
||||
let sut = new Seller(sellerInventory);
|
||||
const overboughtAmount = 1000;
|
||||
const expectedBuyAmount = sut.inventory["Oranges"].startingQuantity;
|
||||
let receipt = sut.sell("Oranges", overboughtAmount);
|
||||
expect(receipt.cost).toEqual(sut.inventory["Oranges"].priceHistory[0] * expectedBuyAmount);
|
||||
expect(receipt.boughtQuantity).toEqual(expectedBuyAmount);
|
||||
})
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user