226 lines
4.9 KiB
Go
226 lines
4.9 KiB
Go
package core
|
|
|
|
import (
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/benbjohnson/clock"
|
|
)
|
|
|
|
func assertEnergy(t *testing.T, s *Savings, total, self, percentage float64) {
|
|
if !compareWithTolerane(s.TotalCharged(), total) {
|
|
t.Errorf("TotalCharged was incorrect, got: %.3f, want: %.3f.", s.TotalCharged(), total)
|
|
}
|
|
if !compareWithTolerane(s.selfConsumptionCharged, self) {
|
|
t.Errorf("ChargedSelfConsumption was incorrect, got: %.3f, want: %.3f.", s.selfConsumptionCharged, self)
|
|
}
|
|
if int(s.SelfConsumptionPercent()) != int(percentage) {
|
|
t.Errorf("SelfConsumptionPercent was incorrect, got: %.1f, want: %.1f.", s.SelfConsumptionPercent(), percentage)
|
|
}
|
|
}
|
|
|
|
func assertPrices(t *testing.T, s *Savings, effectivePrice, savingsAmount float64) {
|
|
if !compareWithTolerane(s.EffectivePrice(), effectivePrice) {
|
|
t.Errorf("EffectivePrice was incorrect, got: %.3f, want: %.3f.", s.EffectivePrice(), effectivePrice)
|
|
}
|
|
if !compareWithTolerane(s.SavingsAmount(), savingsAmount) {
|
|
t.Errorf("SavingsAmount was incorrect, got: %.3f, want: %.3f.", s.SavingsAmount(), savingsAmount)
|
|
}
|
|
}
|
|
|
|
func compareWithTolerane(a, b float64) bool {
|
|
tolerance := 0.001
|
|
diff := math.Abs(a - b)
|
|
return diff < tolerance
|
|
}
|
|
|
|
type StubPublisher struct{}
|
|
|
|
func (p StubPublisher) publish(key string, val interface{}) {}
|
|
|
|
func TestSavingsWithChangingEnergySources(t *testing.T) {
|
|
p := StubPublisher{}
|
|
|
|
clck := clock.NewMock()
|
|
s := &Savings{
|
|
clock: clck,
|
|
started: clck.Now(),
|
|
updated: clck.Now(),
|
|
}
|
|
|
|
tc := []struct {
|
|
title string
|
|
grid, pv, battery, charge float64
|
|
total, self, percentage float64
|
|
}{
|
|
{"half grid, half pv",
|
|
2500, 2500, 0, 5000,
|
|
5, 2.5, 50},
|
|
{"full pv",
|
|
0, 5000, 0, 5000,
|
|
10, 7.5, 75},
|
|
{"full grid",
|
|
5000, 0, 0, 5000,
|
|
15, 7.5, 50},
|
|
{"half grid, half battery",
|
|
2500, 0, 2500, 5000,
|
|
20, 10, 50},
|
|
{"full pv, pv export",
|
|
-5000, 10000, 0, 5000,
|
|
25, 15, 60},
|
|
{"full pv, pv export, battery charge",
|
|
-2500, 10000, -2500, 5000,
|
|
30, 20, 66},
|
|
{"double charge speed, full grid",
|
|
10000, 0, 0, 10000,
|
|
40, 20, 50},
|
|
}
|
|
|
|
s.Update(p, 0, 0, 0, 0)
|
|
|
|
for _, tc := range tc {
|
|
t.Logf("%+v", tc)
|
|
|
|
clck.Add(time.Hour)
|
|
s.Update(p, tc.grid, tc.pv, tc.battery, tc.charge)
|
|
assertEnergy(t, s, tc.total, tc.self, tc.percentage)
|
|
}
|
|
}
|
|
|
|
func TestSavingsWithDifferentTimespans(t *testing.T) {
|
|
p := StubPublisher{}
|
|
|
|
clck := clock.NewMock()
|
|
s := &Savings{
|
|
clock: clck,
|
|
started: clck.Now(),
|
|
updated: clck.Now(),
|
|
}
|
|
|
|
type tcStep = struct {
|
|
dt time.Duration
|
|
grid, pv, battery, charge float64
|
|
}
|
|
|
|
tc := []struct {
|
|
title string
|
|
steps []tcStep
|
|
total, self, percentage float64
|
|
}{
|
|
{"10 second not charging, full grid",
|
|
[]tcStep{
|
|
{10 * time.Second, 1000, 0, 0, 0},
|
|
},
|
|
0, 0, 0, // 0Wh
|
|
},
|
|
{"10 second 11kW charging, full grid",
|
|
[]tcStep{
|
|
{10 * time.Second, 0, 0, 0, 11000},
|
|
},
|
|
0.030556, 0, 0, // 30,555Wh
|
|
},
|
|
{"10 second 11kW charging, full grid",
|
|
[]tcStep{
|
|
{10 * time.Second, 0, 0, 0, 11000},
|
|
},
|
|
0.061111, 0, 0, // 61,111Wh
|
|
},
|
|
{"5x 2 second 11kW charging, full grid",
|
|
[]tcStep{
|
|
{2 * time.Second, 0, 0, 0, 11000},
|
|
{2 * time.Second, 0, 0, 0, 11000},
|
|
{2 * time.Second, 0, 0, 0, 11000},
|
|
{2 * time.Second, 0, 0, 0, 11000},
|
|
{2 * time.Second, 0, 0, 0, 11000},
|
|
},
|
|
0.092, 0, 0, // 91,666Wh
|
|
},
|
|
{"30 min 11kW charging, full grid",
|
|
[]tcStep{
|
|
{30 * time.Minute, 0, 0, 0, 11000},
|
|
},
|
|
5.592, 0, 0, // 5561,111Wh
|
|
},
|
|
{"4 hours 11kW charging, full pv",
|
|
[]tcStep{
|
|
{4 * time.Hour, 0, 11000, 0, 11000},
|
|
},
|
|
49.592, 44, 88,
|
|
},
|
|
}
|
|
|
|
s.Update(p, 0, 0, 0, 0)
|
|
|
|
for _, tc := range tc {
|
|
t.Logf("%+v", tc)
|
|
|
|
for _, tc := range tc.steps {
|
|
clck.Add(tc.dt)
|
|
s.Update(p, tc.grid, tc.pv, tc.battery, tc.charge)
|
|
}
|
|
|
|
assertEnergy(t, s, tc.total, tc.self, tc.percentage)
|
|
}
|
|
}
|
|
|
|
func TestEffectiveEnergyPriceAndSavingsAmount(t *testing.T) {
|
|
p := StubPublisher{}
|
|
|
|
clck := clock.NewMock()
|
|
|
|
type tcStep = struct {
|
|
dt time.Duration
|
|
grid, pv, battery, charge float64
|
|
}
|
|
|
|
tc := []struct {
|
|
title string
|
|
steps []tcStep
|
|
effectivePrice, savingsAmount float64
|
|
}{
|
|
{"1 hour, 10kW, full grid",
|
|
[]tcStep{
|
|
{time.Hour, 10000, 0, 0, 10000},
|
|
},
|
|
0.3, 0,
|
|
},
|
|
{"1 hour, 10kW, full pv",
|
|
[]tcStep{
|
|
{time.Hour, 0, 10000, 0, 10000},
|
|
},
|
|
0.08, 2.2,
|
|
},
|
|
{"1 hour, 10kW, full battery",
|
|
[]tcStep{
|
|
{time.Hour, 0, 0, 10000, 10000},
|
|
},
|
|
0.08, 2.2,
|
|
},
|
|
|
|
{"1 hour, 10kW, half grid, half pv",
|
|
[]tcStep{
|
|
{time.Hour, 5000, 0, 5000, 10000},
|
|
},
|
|
0.19, 1.1,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tc {
|
|
t.Logf("%+v", tc)
|
|
|
|
s := &Savings{
|
|
clock: clck,
|
|
started: clck.Now(),
|
|
updated: clck.Now(),
|
|
}
|
|
s.Update(p, 0, 0, 0, 0)
|
|
|
|
for _, tc := range tc.steps {
|
|
clck.Add(tc.dt)
|
|
s.Update(p, tc.grid, tc.pv, tc.battery, tc.charge)
|
|
}
|
|
|
|
assertPrices(t, s, tc.effectivePrice, tc.savingsAmount)
|
|
}
|
|
}
|