Compare commits
10 Commits
ea83759be2
...
a8157b2f1e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8157b2f1e | ||
|
|
0939bc0c41 | ||
|
|
3e9349078c | ||
|
|
1345071f0a | ||
|
|
03ec3247f9 | ||
|
|
8a7f97d230 | ||
|
|
273b466b43 | ||
|
|
01fda04fde | ||
|
|
114a17245e | ||
|
|
f7dd025347 |
1
.gitignore
vendored
Normal file → Executable file
1
.gitignore
vendored
Normal file → Executable file
@@ -1 +1,2 @@
|
||||
**/*.sw*
|
||||
ledger-ui
|
||||
|
||||
0
balance.go
Normal file → Executable file
0
balance.go
Normal file → Executable file
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
||||
module gogs.inhome.blapointe.com/bel/ledger-ui
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34
|
||||
)
|
||||
|
||||
require gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
8
go.sum
Normal file
8
go.sum
Normal file
@@ -0,0 +1,8 @@
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34 h1:0tuX5dfOksiOQD1vbJjVNVTVxTTIng7UrUdSLF5T+Ao=
|
||||
gogs.inhome.blapointe.com/local/args v0.0.0-20230410154220-44370f257b34/go.mod h1:YG9n3Clg7683ohkVnJK2hdX8bBS9EojIsd1qPZumX0Y=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
18
ledger.go
Normal file → Executable file
18
ledger.go
Normal file → Executable file
@@ -8,6 +8,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Ledger struct {
|
||||
@@ -24,6 +25,21 @@ func NewLedger(path string) (Ledger, error) {
|
||||
}, err
|
||||
}
|
||||
|
||||
func (ledger Ledger) NewTransaction() error {
|
||||
transactions, err := ledger.Transactions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
transactions = append(transactions, Transaction{
|
||||
Date: time.Now().Format("2006-01-02"),
|
||||
Description: "?",
|
||||
Payer: "?",
|
||||
Payee: "?",
|
||||
Amount: 0,
|
||||
})
|
||||
return ledger.SetTransactions(transactions)
|
||||
}
|
||||
|
||||
func (ledger Ledger) SetTransaction(i int, transaction Transaction) error {
|
||||
transactions, err := ledger.Transactions()
|
||||
if err != nil {
|
||||
@@ -37,7 +53,7 @@ func (ledger Ledger) SetTransaction(i int, transaction Transaction) error {
|
||||
}
|
||||
|
||||
func (ledger Ledger) SetTransactions(transactions []Transaction) error {
|
||||
f, err := ioutil.TempFile(os.TempDir(), path.Base(ledger.path)+".*")
|
||||
f, err := ioutil.TempFile(path.Dir(ledger.path), path.Base(ledger.path)+".*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
55
ledger_test.go
Normal file → Executable file
55
ledger_test.go
Normal file → Executable file
@@ -2,7 +2,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
@@ -10,8 +10,19 @@ import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func testLedgerFile(t *testing.T) string {
|
||||
path := path.Join(t.TempDir(), t.Name()+".dat")
|
||||
if b, err := ioutil.ReadFile("./testdata/ledger.dat"); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if err := ioutil.WriteFile(path, b, os.ModePerm); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func TestLedgerTransactionsBalances(t *testing.T) {
|
||||
ledger, err := NewLedger("./testdata/ledger.dat")
|
||||
path := testLedgerFile(t)
|
||||
ledger, err := NewLedger(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -67,21 +78,7 @@ func TestLedgerSetTransaction(t *testing.T) {
|
||||
Amount: 1.23,
|
||||
}
|
||||
}
|
||||
path := path.Join(t.TempDir(), "ledger.dat")
|
||||
a, err := os.Open("./testdata/ledger.dat")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, err := os.Create(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := io.Copy(b, a); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.Close()
|
||||
b.Close()
|
||||
|
||||
path := testLedgerFile(t)
|
||||
ledger, err := NewLedger(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -109,3 +106,27 @@ func TestLedgerSetTransaction(t *testing.T) {
|
||||
t.Fatal(want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLedgerNewTransaction(t *testing.T) {
|
||||
path := testLedgerFile(t)
|
||||
ledger, err := NewLedger(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
transactions, err := ledger.Transactions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantTransactions := append(transactions, newStubTransaction())
|
||||
ledger.NewTransaction()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gotTransactions, err := ledger.Transactions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if fmt.Sprint(wantTransactions) != fmt.Sprint(gotTransactions) {
|
||||
t.Fatal(wantTransactions, gotTransactions)
|
||||
}
|
||||
}
|
||||
|
||||
4
main.go
Normal file → Executable file
4
main.go
Normal file → Executable file
@@ -2,7 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"local/args"
|
||||
"gogs.inhome.blapointe.com/local/args"
|
||||
"log"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -18,6 +19,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
log.Println("listening on", as.GetInt("p"))
|
||||
if err := http.ListenAndServe(":"+fmt.Sprint(as.GetInt("p")), Server{ledger: ledger, debug: as.GetBool("debug")}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
0
public/balances.json
Normal file → Executable file
0
public/balances.json
Normal file → Executable file
79
public/index.html
Normal file → Executable file
79
public/index.html
Normal file → Executable file
@@ -2,6 +2,15 @@
|
||||
<header>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css@2/out/dark.css">
|
||||
<style>
|
||||
body > div {
|
||||
width: 100%;
|
||||
}
|
||||
.center {
|
||||
text-align: center;
|
||||
}
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
#transactions > tbody > tr > td:first-child input {
|
||||
padding: 1ch;
|
||||
display: inline-block;
|
||||
@@ -30,13 +39,22 @@
|
||||
loadBalances(body.Balances)
|
||||
}, null)
|
||||
}
|
||||
function prettyMoney(money) {
|
||||
try {
|
||||
var f = parseFloat(money)
|
||||
if (f) {
|
||||
return Math.round(100.0*f) / 100.0
|
||||
}
|
||||
} catch { }
|
||||
return money;
|
||||
}
|
||||
function loadBalances(balances) {
|
||||
var innerHTML = ""
|
||||
innerHTML += "<table>"
|
||||
|
||||
for(var k in balances) {
|
||||
if(k.startsWith("Asset"))
|
||||
innerHTML += `<tr><td>${k}</td><td>${balances[k]}</td></tr>`
|
||||
innerHTML += `<tr><td>${k}</td><td>${prettyMoney(balances[k])}</td></tr>`
|
||||
}
|
||||
|
||||
innerHTML += "</table>"
|
||||
@@ -68,7 +86,7 @@
|
||||
one += " <tr>"
|
||||
one += " <td></td><td></td>"
|
||||
for(var key of ["Payee", "Amount"])
|
||||
one += ` <td contenteditable key=${JSON.stringify(key)}>${transaction[key]}</td>`
|
||||
one += ` <td contenteditable key=${JSON.stringify(key)}>${prettyMoney(transaction[key])}</td>`
|
||||
one += " </tr>"
|
||||
one += " <tr>"
|
||||
one += " <td></td><td></td>"
|
||||
@@ -84,6 +102,25 @@
|
||||
|
||||
document.getElementById("transactions").innerHTML = innerHTML
|
||||
}
|
||||
function newTransaction() {
|
||||
var today = new Date()
|
||||
var year = today.getFullYear()
|
||||
var month = today.getMonth()+1
|
||||
var day = today.getDate()
|
||||
if (day < 10) {
|
||||
day = `0${day}`
|
||||
}
|
||||
if (month < 10) {
|
||||
month = `0${month}`
|
||||
}
|
||||
http("post", "/api/transactions", () => {init()}, JSON.stringify({
|
||||
Date: `${year}-${month}-${day}`,
|
||||
Description: "?",
|
||||
Payer: "?",
|
||||
Payee: "?",
|
||||
Amount: 0,
|
||||
}))
|
||||
}
|
||||
function saveTransaction(row) {
|
||||
const inputs = row.getElementsByTagName("td")
|
||||
var kvs = {}
|
||||
@@ -92,29 +129,48 @@
|
||||
if (!key) {
|
||||
continue
|
||||
}
|
||||
const value = inputs[i].innerHTML
|
||||
const value = (inputs[i].textContent || inputs[i].innerText).replaceAll("\n", " ").trim()
|
||||
kvs[key] = value
|
||||
if (!isNaN(value))
|
||||
kvs[key] = parseFloat(value)
|
||||
}
|
||||
http("put", "/api/transactions", () => {init()}, JSON.stringify(kvs))
|
||||
}
|
||||
function setRowKeyValue(row, wantkey, wantvalue) {
|
||||
function getRowKeyValue(row, wantkey) {
|
||||
const inputs = row.getElementsByTagName("td")
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
const key = inputs[i].getAttribute("key")
|
||||
if (key == wantkey) {
|
||||
return inputs[i].innerText + ""
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
function setRowKeyValues(row, wantKeysWantValues) {
|
||||
const inputs = row.getElementsByTagName("td")
|
||||
var kvs = {}
|
||||
for (var i = 0; i < inputs.length; i++) {
|
||||
const key = inputs[i].getAttribute("key")
|
||||
if (key == wantkey) {
|
||||
inputs[i].innerHTML = wantvalue
|
||||
break
|
||||
if (key in wantKeysWantValues) {
|
||||
inputs[i].innerHTML = wantKeysWantValues[key]
|
||||
}
|
||||
}
|
||||
saveTransaction(row)
|
||||
}
|
||||
zachsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Zach")
|
||||
belsPayment = (x) => setRowKeyValue(x, "Payer", "AssetAccount:Bel")
|
||||
zachsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Zach")
|
||||
belsCharge = (x) => setRowKeyValue(x, "Payee", "AssetAccount:Bel")
|
||||
zachsPayment = (x) => {
|
||||
setRowKeyValues(x, {
|
||||
"Payee": getRowKeyValue(x, "Payer"),
|
||||
"Payer": "AssetAccount:Zach",
|
||||
})
|
||||
}
|
||||
belsPayment = (x) => {
|
||||
setRowKeyValues(x, {
|
||||
"Payee": getRowKeyValue(x, "Payer"),
|
||||
"Payer": "AssetAccount:Bel",
|
||||
})
|
||||
}
|
||||
zachsCharge = (x) => setRowKeyValues(x, {"Payee": "AssetAccount:Zach"})
|
||||
belsCharge = (x) => setRowKeyValues(x, {"Payee": "AssetAccount:Bel"})
|
||||
function http(method, remote, callback, body) {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
xmlhttp.onreadystatechange = function() {
|
||||
@@ -136,6 +192,7 @@
|
||||
<summary>Balances</summary>
|
||||
<div id="balances"></div>
|
||||
</details>
|
||||
<div class="right"><button onclick="newTransaction();">New Transaction</button></div>
|
||||
<table id="transactions"></table>
|
||||
</body>
|
||||
<footer>
|
||||
|
||||
26
server.go
Normal file → Executable file
26
server.go
Normal file → Executable file
@@ -22,6 +22,8 @@ func (server Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method + r.URL.Path {
|
||||
case "GET/api/transactions":
|
||||
server.getTransactions(w, r)
|
||||
case "POST/api/transactions":
|
||||
server.postTransactions(w, r)
|
||||
case "PUT/api/transactions":
|
||||
server.putTransactions(w, r)
|
||||
case "GET/api/balances":
|
||||
@@ -50,6 +52,30 @@ func (server Server) getTransactions(w http.ResponseWriter, r *http.Request) {
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"Transactions": transactions})
|
||||
}
|
||||
|
||||
func (server Server) postTransactions(w http.ResponseWriter, r *http.Request) {
|
||||
var request struct {
|
||||
Transaction
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
|
||||
server.err(w, r, err)
|
||||
return
|
||||
}
|
||||
if err := server.ledger.NewTransaction(); err != nil {
|
||||
server.err(w, r, err)
|
||||
return
|
||||
}
|
||||
transactions, err := server.ledger.Transactions()
|
||||
if err != nil {
|
||||
server.err(w, r, err)
|
||||
return
|
||||
}
|
||||
if err := server.ledger.SetTransaction(len(transactions)-1, request.Transaction); err != nil {
|
||||
server.err(w, r, err)
|
||||
return
|
||||
}
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{"ok": true})
|
||||
}
|
||||
|
||||
func (server Server) putTransactions(w http.ResponseWriter, r *http.Request) {
|
||||
var request struct {
|
||||
IDX int `json:"idx"`
|
||||
|
||||
48
server_test.go
Normal file
48
server_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerPostTransactions(t *testing.T) {
|
||||
path := testLedgerFile(t)
|
||||
ledger, err := NewLedger(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
server := Server{ledger: ledger}
|
||||
|
||||
r := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{
|
||||
"Date": "2099-02-03",
|
||||
"Description": "test",
|
||||
"Payer": "payer",
|
||||
"Payee": "payee",
|
||||
"Amount": 1.02
|
||||
}`))
|
||||
w := httptest.NewRecorder()
|
||||
server.postTransactions(w, r)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatal(w.Code)
|
||||
}
|
||||
if s := strings.TrimSpace(string(w.Body.Bytes())); s != `{"ok":true}` {
|
||||
t.Fatal(s)
|
||||
}
|
||||
|
||||
transactions, err := ledger.Transactions()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if transactions[len(transactions)-1] != (Transaction{
|
||||
Date: "2099-02-03",
|
||||
Description: "test",
|
||||
Payer: "payer",
|
||||
Payee: "payee",
|
||||
Amount: 1.02,
|
||||
}) {
|
||||
t.Fatal(transactions[len(transactions)-1])
|
||||
}
|
||||
}
|
||||
75
testdata/2018-.b.dat
vendored
Normal file
75
testdata/2018-.b.dat
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
2021-08-07 UTAH INFRASTRUCTURE
|
||||
Withdrawal:UTAHINFRASTRUCTURE $30.00
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-07 HARMONS - OREM
|
||||
Withdrawal:HARMONSOREM $8.73
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-07 MACEY'S 8TH NORTH
|
||||
Withdrawal:MACEYS8THNORTH $18.05
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-07 BEEHIVE BROADBAND LL
|
||||
Withdrawal:BEEHIVEBROADBANDLL $56.38
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-07 CULLIGAN WATER CONDI
|
||||
Withdrawal:CULLIGANWATERCONDI $35.00
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-09 Payment
|
||||
Withdrawal:Payment $121.38
|
||||
AssetAccount:Bel
|
||||
2021-08-12 HARMONS - OREM
|
||||
Withdrawal:HARMONSOREM $12.82
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-12 COSTCO WHSE #0484
|
||||
Withdrawal:COSTCOWHSE0484 $76.02
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-12 TRADER JOE'S #352
|
||||
Withdrawal:TRADERJOES352 $64.57
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-12 TARGET T-1754
|
||||
Withdrawal:TARGETT1754 $56.12
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-13 TARGET T-1754
|
||||
Withdrawal:TARGETT1754 $44.66
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-15 TRADER JOE'S #352
|
||||
Withdrawal:TRADERJOES352 $7.70
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-15 WALGREENS #11150
|
||||
Withdrawal:WALGREENS11150 $16.96
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-15 HARMONS - OREM
|
||||
Withdrawal:HARMONSOREM $35.30
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-16 Payment
|
||||
Withdrawal:Payment $140.49
|
||||
AssetAccount:Bel
|
||||
2021-08-17 Payment
|
||||
Withdrawal:Payment $20.63
|
||||
AssetAccount:Bel
|
||||
2021-08-19 REDMOND FARMS STORE
|
||||
Withdrawal:REDMONDFARMSSTORE $42.07
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-19 TRADER JOE'S #352
|
||||
Withdrawal:TRADERJOES352 $52.93
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-19 COSTCO WHSE #0484
|
||||
Withdrawal:COSTCOWHSE0484 $140.93
|
||||
AssetAccount:Chase:1049
|
||||
2021-08-23 Payment
|
||||
Withdrawal:Payment $457.21
|
||||
AssetAccount:Bel
|
||||
2021-08-28 TARGET T-1754
|
||||
Withdrawal:TARGETT1754 $95.34
|
||||
AssetAccount:Chase:1049
|
||||
2021-09-01 MACEY'S 8TH NORTH
|
||||
Withdrawal:MACEYS8THNORTH $16.41
|
||||
AssetAccount:Chase:1049
|
||||
2021-09-01 COSTCO WHSE #0484
|
||||
Withdrawal:COSTCOWHSE0484 $167.81
|
||||
AssetAccount:Chase:1049
|
||||
2021-09-03 WALGREENS #12294
|
||||
Withdrawal:WALGREENS12294 $16.60
|
||||
AssetAccount:Chase:1049
|
||||
2021-09-03 TST* COSTA VIDA - 01
|
||||
Withdrawal:TSTCOSTAVIDA01 $20.78
|
||||
AssetAccount:Chase:1049
|
||||
2
testdata/ledger-groceries-custom.dat
vendored
Normal file → Executable file
2
testdata/ledger-groceries-custom.dat
vendored
Normal file → Executable file
@@ -1,5 +1,5 @@
|
||||
2021-01-01 groceries
|
||||
Withdrawal:Target $10
|
||||
Withdrawal:Target $10.0000001
|
||||
AssetAccount:Chase:8824
|
||||
2021-01-05 bel vidya
|
||||
Withdrawal:Target $1
|
||||
|
||||
0
testdata/ledger.dat
vendored
Normal file → Executable file
0
testdata/ledger.dat
vendored
Normal file → Executable file
0
testdata/ledger.json
vendored
Normal file → Executable file
0
testdata/ledger.json
vendored
Normal file → Executable file
15
transaction.go
Normal file → Executable file
15
transaction.go
Normal file → Executable file
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Transaction struct {
|
||||
@@ -18,6 +19,20 @@ type Transaction struct {
|
||||
Amount float32
|
||||
}
|
||||
|
||||
const (
|
||||
UnknownAccount = "?"
|
||||
)
|
||||
|
||||
func newStubTransaction() Transaction {
|
||||
return Transaction{
|
||||
Date: time.Now().Format("2006-01-02"),
|
||||
Description: UnknownAccount,
|
||||
Payer: UnknownAccount,
|
||||
Payee: UnknownAccount,
|
||||
Amount: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func readTransaction(r io.Reader) (Transaction, error) {
|
||||
lines := make([][]byte, 3)
|
||||
for i := range lines {
|
||||
|
||||
18
transaction_test.go
Normal file → Executable file
18
transaction_test.go
Normal file → Executable file
@@ -6,6 +6,24 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStubTransaction(t *testing.T) {
|
||||
want := Transaction{
|
||||
Date: "1",
|
||||
Amount: 0,
|
||||
Payer: UnknownAccount,
|
||||
Payee: UnknownAccount,
|
||||
Description: UnknownAccount,
|
||||
}
|
||||
got := newStubTransaction()
|
||||
if got.Date == "" {
|
||||
t.Fatal(got.Date)
|
||||
}
|
||||
got.Date = want.Date
|
||||
if want != got {
|
||||
t.Fatal(want, got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWords(t *testing.T) {
|
||||
input := `
|
||||
hello world
|
||||
|
||||
Reference in New Issue
Block a user