Skip to content

Commit

Permalink
fix fake /api/returns endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
crhntr committed Dec 25, 2023
1 parent 2857ca8 commit 8929748
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 111 deletions.
18 changes: 18 additions & 0 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"os"
"strings"

"go.mongodb.org/mongo-driver/bson/primitive"

"github.com/portfoliotree/portfolio/returns"
)

Expand Down Expand Up @@ -57,6 +59,22 @@ func (pf *Specification) AssetReturns(ctx context.Context) (returns.Table, error
return doJSONRequest[returns.Table](http.DefaultClient.Do, req)
}

func ParseComponentsFromURL(values url.Values, prefix string) ([]Component, error) {
assetValues, ok := values[prefix+"-id"]
if !ok {
return nil, errors.New("use asset-id parameters to specify asset returns")
}
components := make([]Component, 0, len(assetValues))
for _, v := range assetValues {
if _, err := primitive.ObjectIDFromHex(v); err == nil {
components = append(components, Component{Type: "Portfolio", ID: v})
continue
}
components = append(components, Component{Type: "Security", ID: v})
}
return components, nil
}

func doJSONRequest[T any](do func(r *http.Request) (*http.Response, error), req *http.Request) (T, error) {
var result T
req.Header.Set("accept", "application/json")
Expand Down
24 changes: 24 additions & 0 deletions api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,37 @@ package portfolio_test

import (
"context"
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/portfoliotree/portfolio"
)

func Test_APIEndpoints(t *testing.T) {
if value, found := os.LookupEnv("CI"); !found || value != "true" {
t.Skip("Skipping test in CI environment")
}

t.Run("returns", func(t *testing.T) {
pf := portfolio.Specification{
Assets: []portfolio.Component{
{ID: "AAPL"},
{ID: "GOOG"},
},
}
table, err := pf.AssetReturns(context.Background())
assert.NoError(t, err)
if table.NumberOfColumns() != 2 {
t.Errorf("Expected 2 columns, got %d", table.NumberOfColumns())
}
if table.NumberOfRows() < 10 {
t.Errorf("Expected at least 10 rows, got %d", table.NumberOfRows())
}
})
}

func TestSpecification_AssetReturns(t *testing.T) {
for _, tt := range []struct {
Name string
Expand Down
115 changes: 4 additions & 111 deletions portfolio_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"testing"
Expand All @@ -32,12 +31,12 @@ func TestMain(m *testing.M) {

func testdataAssetReturns(crp portfolio.ComponentReturnsProvider) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
var pf portfolio.Specification
if err := pf.ParseValues(req.URL.Query()); err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
assets, err := portfolio.ParseComponentsFromURL(req.URL.Query(), "asset")
if err != nil {
http.Error(res, err.Error(), http.StatusBadRequest)
return
}
table, err := crp.ComponentReturnsTable(req.Context(), pf.Assets...)
table, err := crp.ComponentReturnsTable(req.Context(), assets...)
if err != nil {
http.Error(res, err.Error(), http.StatusInternalServerError)
return
Expand Down Expand Up @@ -294,112 +293,6 @@ func Test_Portfolio_Validate(t *testing.T) {
}
}

func Test_Portfolio_ParseValues(t *testing.T) {
for _, tt := range []struct {
Name string
Values url.Values
In, Out portfolio.Specification
ExpectErr bool
}{
{
Name: "set everything",
Values: url.Values{
"name": []string{"X"},
"asset-id": []string{"y", "z"},
"benchmark-id": []string{"b"},
"filepath": []string{"f"},
"policy-weight": []string{".5", ".5"},
"policy-rebalance": []string{"Daily"},
"policy-weights-algorithm": []string{"Static"},
"policy-update-weights": []string{"Daily"},
"policy-weight-algorithm-look-back": []string{"1 Week"},
},
Out: portfolio.Specification{
Name: "X",
Assets: []portfolio.Component{
{ID: "y"},
{ID: "z"},
},
Benchmark: portfolio.Component{
ID: "b",
},
Filepath: "f",
Policy: portfolio.Policy{
RebalancingInterval: "Daily",
WeightsAlgorithm: "Static",
Weights: []float64{0.5, 0.5},
WeightsUpdatingInterval: "Daily",
WeightsAlgorithmLookBack: "1 Week",
},
},
},
{
Name: "empty values do not override",
Values: url.Values{},
In: portfolio.Specification{
Name: "no change",
Benchmark: portfolio.Component{ID: "b"},
Assets: []portfolio.Component{{ID: "a1"}},
Filepath: "f",
},
Out: portfolio.Specification{
Name: "no change",
Benchmark: portfolio.Component{ID: "b"},
Assets: []portfolio.Component{{ID: "a1"}},
Filepath: "f",
},
},
} {
t.Run(tt.Name, func(t *testing.T) {
pf := &tt.In
err := pf.ParseValues(tt.Values)
if tt.ExpectErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.Out, *pf)
})
}
}

func Test_Portfolio_Values(t *testing.T) {
t.Run("encode and decode", func(t *testing.T) {
pf := portfolio.Specification{
Name: "X",
Assets: []portfolio.Component{
{ID: "y"},
{ID: "z"},
},
Benchmark: portfolio.Component{
ID: "b",
},
Filepath: "f",
Policy: portfolio.Policy{
RebalancingInterval: "Daily",
WeightsAlgorithm: "Static",
Weights: []float64{0.5, 0.5},
WeightsUpdatingInterval: "Daily",
WeightsAlgorithmLookBack: "1 Week",
},
}

var update portfolio.Specification
e := pf.Values().Encode()
q, err := url.ParseQuery(e)
require.NoError(t, err)
assert.NoError(t, update.ParseValues(q))
assert.Equal(t, pf, update)
})

t.Run("fail to parse float", func(t *testing.T) {
values, err := url.ParseQuery(`policy-weight=x`)
require.NoError(t, err)
var pf portfolio.Specification
assert.Error(t, pf.ParseValues(values))
})
}

func TestPortfolio_RemoveAsset(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var zero portfolio.Specification
Expand Down

0 comments on commit 8929748

Please sign in to comment.