commit de58e42f472aa2cdf915c17dae43c0604ed54d29
parent 0478a7a1d81fcf63168dd53b46e52ebb87c42269
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Fri, 25 Apr 2025 22:00:19 +0200
refactoring for testing
Diffstat:
4 files changed, 106 insertions(+), 71 deletions(-)
diff --git a/cmd/taldir-server/main.go b/cmd/taldir-server/main.go
@@ -28,10 +28,13 @@ package main
import (
"flag"
+ "fmt"
"log"
"net/http"
"os"
+ "gopkg.in/ini.v1"
+ "gorm.io/driver/postgres"
taldir "taler.net/taldir/pkg/rest"
)
@@ -42,7 +45,7 @@ var (
)
func handleRequests(t *taldir.Taldir) {
- log.Fatal(http.ListenAndServe(t.Cfg.Section("taldir").Key("bind_to").MustString("localhost:11000"), t.Router))
+ log.Fatal(http.ListenAndServe(t.Cfg.Ini.Section("taldir").Key("bind_to").MustString("localhost:11000"), t.Router))
}
func main() {
@@ -55,7 +58,24 @@ func main() {
cfgfile = *cfgFlag
}
t := taldir.Taldir{}
- t.Initialize(cfgfile, version, taldirdatahome)
- t.Cfg.WriteTo(os.Stdout)
+ cfg, err := ini.LooseLoad(cfgfile)
+ if err != nil {
+ log.Fatalf("Failed to read config: %v", err)
+ os.Exit(1)
+ }
+ psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
+ cfg.Section("taldir-pq").Key("host").MustString("localhost"),
+ cfg.Section("taldir-pq").Key("port").MustInt64(5432),
+ cfg.Section("taldir-pq").Key("user").MustString("taldir"),
+ cfg.Section("taldir-pq").Key("password").MustString("secret"),
+ cfg.Section("taldir-pq").Key("db_name").MustString("taldir"))
+ db := postgres.Open(psqlconn)
+ cfg.WriteTo(os.Stdout)
+ t.Initialize(taldir.TaldirConfig{
+ Ini: cfg,
+ Version: version,
+ Datahome: taldirdatahome,
+ Db: db,
+ })
handleRequests(&t)
}
diff --git a/cmd/taldir-server/main_test.go b/cmd/taldir-server/main_test.go
@@ -21,7 +21,8 @@ package main_test
import (
"bytes"
"crypto/sha512"
- "io/ioutil"
+ "io"
+ "log"
"net/http"
"net/http/httptest"
"os"
@@ -31,6 +32,8 @@ import (
gnunetutil "gnunet/util"
"github.com/jarcoal/httpmock"
+ "gopkg.in/ini.v1"
+ "gorm.io/driver/sqlite"
_ "taler.net/taldir/cmd/taldir-server"
"taler.net/taldir/internal/util"
taldir "taler.net/taldir/pkg/rest"
@@ -72,7 +75,19 @@ var newOrderStatusUnpaidMockResponse = `
`
func TestMain(m *testing.M) {
- t.Initialize("testdata/taldir-test.conf")
+ cfg, err := ini.LooseLoad("testdata/taldir-test.conf")
+ if err != nil {
+ log.Fatalf("Failed to read config: %v", err)
+ os.Exit(1)
+ }
+ db := sqlite.Open("file::memory:?cache=shared")
+ t.Initialize(taldir.TaldirConfig{
+ Ini: cfg,
+ Version: "testing",
+ Datahome: "../../",
+ Db: db,
+ })
+ log.Println("hello")
code := m.Run()
t.ClearDatabase()
os.Exit(code)
@@ -115,7 +130,7 @@ func TestRegisterRequest(s *testing.T) {
if err != nil {
s.Errorf("No validation code file found!\n")
}
- code, err := ioutil.ReadAll(file)
+ code, err := io.ReadAll(file)
if err != nil {
s.Errorf("Error reading validation code file contents!\n")
}
@@ -149,7 +164,7 @@ func TestRegisterQRPageRequest(s *testing.T) {
if err != nil {
s.Errorf("No validation code file found!\n")
}
- code, err := ioutil.ReadAll(file)
+ code, err := io.ReadAll(file)
if err != nil {
s.Errorf("Error reading validation code file contents!\n")
}
@@ -175,7 +190,7 @@ func TestReRegisterRequest(s *testing.T) {
if err != nil {
s.Errorf("No validation code file found!\n")
}
- code, err := ioutil.ReadAll(file)
+ code, err := io.ReadAll(file)
if err != nil {
s.Errorf("Error reading validation code file contents!\n")
}
@@ -275,7 +290,7 @@ func TestRegisterRequestWrongPubkey(s *testing.T) {
if err != nil {
s.Errorf("No validation code file found!\n")
}
- code, err := ioutil.ReadAll(file)
+ code, err := io.ReadAll(file)
if err != nil {
s.Errorf("Error reading validation code file contents!\n")
}
diff --git a/go.mod b/go.mod
@@ -20,23 +20,21 @@ require (
require (
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/bfix/gospel v1.2.31 // indirect
- github.com/jackc/chunkreader/v2 v2.0.1 // indirect
- github.com/jackc/pgconn v1.14.3 // indirect
- github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
- github.com/jackc/pgproto3/v2 v2.3.3 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
- github.com/jackc/pgtype v1.14.4 // indirect
- github.com/jackc/pgx/v4 v4.18.3 // indirect
github.com/jackc/pgx/v5 v5.7.4 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
+ github.com/kr/text v0.1.0 // indirect
+ github.com/mattn/go-sqlite3 v1.14.22 // indirect
+ github.com/rogpeppe/go-internal v1.14.1 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sync v0.13.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
+ gorm.io/driver/sqlite v1.5.7 // indirect
)
replace gnunet v0.1.27 => ./third_party/gnunet-go/src/gnunet
diff --git a/pkg/rest/taldir.go b/pkg/rest/taldir.go
@@ -49,13 +49,29 @@ import (
talerutil "github.com/schanzen/taler-go/pkg/util"
"github.com/skip2/go-qrcode"
"gopkg.in/ini.v1"
- "gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"taler.net/taldir/internal/gana"
"taler.net/taldir/internal/util"
)
+type TaldirConfig struct {
+ // The config to use
+ Ini *ini.File
+
+ // The Taldir Version
+ Version string
+
+ // The Taldir data home location (usually $datadir/taldir)
+ Datahome string
+
+ // The database connection to use
+ Db gorm.Dialector
+
+ // The merchant connection to use
+ Merchant merchant.Merchant
+}
+
// Taldir is the primary object of the Taldir service
type Taldir struct {
@@ -66,10 +82,7 @@ type Taldir struct {
Db *gorm.DB
// Our configuration from the config.json
- Cfg *ini.File
-
- // TalDir version
- Version string
+ Cfg TaldirConfig
// Map of supported validators as defined in the configuration
Validators map[string]Validator
@@ -645,7 +658,7 @@ func (t *Taldir) configResponse(w http.ResponseWriter, r *http.Request) {
cfg := VersionResponse{
Version: "0:0:0",
Name: "taler-directory",
- MonthlyFee: t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"),
+ MonthlyFee: t.Cfg.Ini.Section("taldir").Key("monthly_fee").MustString("KUDOS:1"),
Methods: meths,
}
w.Header().Set("Content-Type", "application/json")
@@ -703,7 +716,7 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
encodedPng := base64.StdEncoding.EncodeToString(png)
fullData := map[string]interface{}{
- "version": t.Version,
+ "version": t.Cfg.Version,
"QRCode": template.URL("data:image/png;base64," + encodedPng),
"WalletLink": template.URL(walletLink),
"productDisclaimerShort": template.HTML(t.I18n.GetLocale(r).GetMessage("productDisclaimerShort")),
@@ -718,7 +731,7 @@ func (t *Taldir) validationPage(w http.ResponseWriter, r *http.Request) {
confirmDeletionOrRegistration = t.I18n.GetLocale(r).GetMessage("confirmReg", address, validation.TargetUri)
}
fullData := map[string]interface{}{
- "version": t.Version,
+ "version": t.Cfg.Version,
"error": r.URL.Query().Get("error"),
"target_uri": template.URL(validation.TargetUri),
"address": template.URL(address),
@@ -740,11 +753,11 @@ func (t *Taldir) ClearDatabase() {
}
func (t *Taldir) termsResponse(w http.ResponseWriter, r *http.Request) {
- tos.ServiceTermsResponse(t.Cfg.Section("taldir"), w, r)
+ tos.ServiceTermsResponse(t.Cfg.Ini.Section("taldir"), w, r)
}
func (t *Taldir) privacyResponse(w http.ResponseWriter, r *http.Request) {
- tos.PrivacyPolicyResponse(t.Cfg.Section("taldir"), w, r)
+ tos.PrivacyPolicyResponse(t.Cfg.Ini.Section("taldir"), w, r)
}
func (t *Taldir) landingPage(w http.ResponseWriter, r *http.Request) {
@@ -752,7 +765,7 @@ func (t *Taldir) landingPage(w http.ResponseWriter, r *http.Request) {
translateFunc := t.I18n.GetLocale(r).GetMessage
fullData := map[string]interface{}{
"validators": t.Validators,
- "version": t.Version,
+ "version": t.Cfg.Version,
"lookupOrRegisterCardTitle": template.HTML(translateFunc("lookup")),
"selectAliasToLookupCardText": template.HTML(translateFunc("selectAliasToLookup")),
"registerCardText": template.HTML(translateFunc("howtoRegisterOrModify")),
@@ -772,7 +785,7 @@ func (t *Taldir) imprintPage(w http.ResponseWriter, r *http.Request) {
translateFunc := t.I18n.GetLocale(r).GetMessage
fullData := map[string]interface{}{
"validators": t.Validators,
- "version": t.Version,
+ "version": t.Cfg.Version,
"productDisclaimerShort": template.HTML(translateFunc("productDisclaimerShort")),
"error": translateFunc(r.URL.Query().Get("error")),
"tr": translateFunc,
@@ -789,7 +802,7 @@ func (t *Taldir) aboutPage(w http.ResponseWriter, r *http.Request) {
translateFunc := t.I18n.GetLocale(r).GetMessage
fullData := map[string]interface{}{
"validators": t.Validators,
- "version": t.Version,
+ "version": t.Cfg.Version,
"productDisclaimerShort": template.HTML(translateFunc("productDisclaimerShort")),
"productDisclaimer": template.HTML(translateFunc("productDisclaimer")),
"error": translateFunc(r.URL.Query().Get("error")),
@@ -836,7 +849,7 @@ func (t *Taldir) methodLookupResultPage(w http.ResponseWriter, r *http.Request)
}
}
fullData := map[string]interface{}{
- "version": t.Version,
+ "version": t.Cfg.Version,
"available": !found,
"method": val.Name,
"address": r.URL.Query().Get("address"),
@@ -863,7 +876,7 @@ func (t *Taldir) methodLandingPage(w http.ResponseWriter, r *http.Request) {
return
}
fullData := map[string]interface{}{
- "version": t.Version,
+ "version": t.Cfg.Version,
"error": r.URL.Query().Get("error"),
"productDisclaimerShort": template.HTML(t.I18n.GetLocale(r).GetMessage("productDisclaimerShort")),
"tr": t.I18n.GetLocale(r).GetMessage,
@@ -928,31 +941,26 @@ func (t *Taldir) getFileName(relativeFileName string, datahome string) string {
}
// Initialize the Taldir instance with cfgfile
-func (t *Taldir) Initialize(cfgfile string, version string, datahome string) {
- _cfg, err := ini.LooseLoad(cfgfile)
- if err != nil {
- log.Fatalf("Failed to read config: %v", err)
- os.Exit(1)
- }
- t.Cfg = _cfg
+func (t *Taldir) Initialize(cfg TaldirConfig) {
+ t.Cfg = cfg
// FIXME localedir
- t.I18n, err = i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{
+ i18n, err := i18n.New(i18n.Glob("./locales/*/*", i18n.LoaderConfig{
// Set custom functions per locale!
Funcs: getFuncs,
}), "en-US", "de-DE")
if err != nil {
panic(err)
}
- if t.Cfg.Section("taldir").Key("production").MustBool(false) {
+ t.I18n = i18n
+ if t.Cfg.Ini.Section("taldir").Key("production").MustBool(false) {
fmt.Println("Production mode enabled")
}
- navTplFile := t.Cfg.Section("taldir").Key("navigation").MustString(t.getFileName("web/templates/nav.html", datahome))
- footerTplFile := t.Cfg.Section("taldir").Key("footer").MustString(t.getFileName("web/templates/footer.html", datahome))
- t.Version = version
- t.BaseUrl = t.Cfg.Section("taldir").Key("base_url").MustString("http://localhost:11000")
+ navTplFile := cfg.Ini.Section("taldir").Key("navigation").MustString(t.getFileName("web/templates/nav.html", cfg.Datahome))
+ footerTplFile := cfg.Ini.Section("taldir").Key("footer").MustString(t.getFileName("web/templates/footer.html", cfg.Datahome))
+ t.BaseUrl = cfg.Ini.Section("taldir").Key("base_url").MustString("http://localhost:11000")
t.Validators = make(map[string]Validator)
- for _, sec := range t.Cfg.Sections() {
+ for _, sec := range cfg.Ini.Sections() {
if !strings.HasPrefix(sec.Name(), "taldir-validator-") {
continue
}
@@ -961,7 +969,7 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) {
continue
}
vname := strings.TrimPrefix(sec.Name(), "taldir-validator-")
- vlandingPageTplFile := sec.Key("registration_page").MustString(t.getFileName("web/templates/landing_"+vname+".html", datahome))
+ vlandingPageTplFile := sec.Key("registration_page").MustString(t.getFileName("web/templates/landing_"+vname+".html", cfg.Datahome))
vlandingPageTpl, err := template.ParseFiles(vlandingPageTplFile, navTplFile, footerTplFile)
if err != nil {
log.Printf("`%s` template not found, disabling validator `%s`.\n", vlandingPageTplFile, vname)
@@ -977,33 +985,27 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) {
ValidAliasRegex: sec.Key("valid_alias_regex").MustString(""),
}
}
- t.ChallengeBytes = t.Cfg.Section("taldir").Key("challenge_bytes").MustInt(16)
- t.ValidationInitiationMax = t.Cfg.Section("taldir").Key("validation_initiation_max").MustInt64(3)
- t.SolutionAttemptsMax = t.Cfg.Section("taldir").Key("solution_attempt_max").MustInt(3)
+ t.ChallengeBytes = cfg.Ini.Section("taldir").Key("challenge_bytes").MustInt(16)
+ t.ValidationInitiationMax = cfg.Ini.Section("taldir").Key("validation_initiation_max").MustInt64(3)
+ t.SolutionAttemptsMax = cfg.Ini.Section("taldir").Key("solution_attempt_max").MustInt(3)
- validationTTLStr := t.Cfg.Section("taldir").Key("validation_timeframe").MustString("5m")
- t.ValidPMSRegex = t.Cfg.Section("taldir").Key("valid_payment_system_address_regex").MustString("[A-Z]+")
+ validationTTLStr := cfg.Ini.Section("taldir").Key("validation_timeframe").MustString("5m")
+ t.ValidPMSRegex = cfg.Ini.Section("taldir").Key("valid_payment_system_address_regex").MustString("[A-Z]+")
t.ValidationTimeframe, err = time.ParseDuration(validationTTLStr)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- retryTimeframeStr := t.Cfg.Section("taldir").Key("solution_attempt_timeframe").MustString("1h")
+ retryTimeframeStr := cfg.Ini.Section("taldir").Key("solution_attempt_timeframe").MustString("1h")
t.SolutionTimeframe, err = time.ParseDuration(retryTimeframeStr)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- t.MonthlyFee = t.Cfg.Section("taldir").Key("monthly_fee").MustString("KUDOS:0")
-
- psqlconn := fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=disable",
- t.Cfg.Section("taldir-pq").Key("host").MustString("localhost"),
- t.Cfg.Section("taldir-pq").Key("port").MustInt64(5432),
- t.Cfg.Section("taldir-pq").Key("user").MustString("taldir"),
- t.Cfg.Section("taldir-pq").Key("password").MustString("secret"),
- t.Cfg.Section("taldir-pq").Key("db_name").MustString("taldir"))
- _db, err := gorm.Open(postgres.Open(psqlconn), &gorm.Config{
+ t.MonthlyFee = cfg.Ini.Section("taldir").Key("monthly_fee").MustString("KUDOS:0")
+
+ _db, err := gorm.Open(cfg.Db, &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
@@ -1016,60 +1018,60 @@ func (t *Taldir) Initialize(cfgfile string, version string, datahome string) {
if err := t.Db.AutoMigrate(&Validation{}); err != nil {
panic(err)
}
- if t.Cfg.Section("taldir").Key("purge_mappings_on_startup_dangerous").MustBool(false) {
+ if cfg.Ini.Section("taldir").Key("purge_mappings_on_startup_dangerous").MustBool(false) {
log.Println("DANGER Purging mappings!")
tx := t.Db.Where("1 = 1").Delete(&Entry{})
log.Printf("Deleted %d entries.\n", tx.RowsAffected)
}
// Clean up validations
- validationExpStr := t.Cfg.Section("taldir").Key("validation_expiration").MustString("24h")
+ validationExpStr := cfg.Ini.Section("taldir").Key("validation_expiration").MustString("24h")
validationExp, err := time.ParseDuration(validationExpStr)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
go func() {
- for true {
+ for {
tx := t.Db.Where("created_at < ?", time.Now().Add(-validationExp)).Delete(&Validation{})
log.Printf("Cleaned up %d stale validations.\n", tx.RowsAffected)
time.Sleep(validationExp)
}
}()
- imprintTplFile := t.Cfg.Section("taldir").Key("imprint_page").MustString(t.getFileName("web/templates/imprint.html", datahome))
+ imprintTplFile := cfg.Ini.Section("taldir").Key("imprint_page").MustString(t.getFileName("web/templates/imprint.html", cfg.Datahome))
t.ImprintTpl, err = template.ParseFiles(imprintTplFile, navTplFile, footerTplFile)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- validationLandingTplFile := t.Cfg.Section("taldir").Key("validation_landing").MustString(t.getFileName("web/templates/validation_landing.html", datahome))
+ validationLandingTplFile := cfg.Ini.Section("taldir").Key("validation_landing").MustString(t.getFileName("web/templates/validation_landing.html", cfg.Datahome))
t.ValidationTpl, err = template.ParseFiles(validationLandingTplFile, navTplFile, footerTplFile)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- landingTplFile := t.Cfg.Section("taldir").Key("landing_page").MustString(t.getFileName("web/templates/landing.html", datahome))
+ landingTplFile := cfg.Ini.Section("taldir").Key("landing_page").MustString(t.getFileName("web/templates/landing.html", cfg.Datahome))
t.LandingPageTpl, err = template.ParseFiles(landingTplFile, navTplFile, footerTplFile)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- lookupResultTplFile := t.Cfg.Section("taldir").Key("lookup_result_page").MustString(t.getFileName("web/templates/lookup_result.html", datahome))
+ lookupResultTplFile := cfg.Ini.Section("taldir").Key("lookup_result_page").MustString(t.getFileName("web/templates/lookup_result.html", cfg.Datahome))
t.LookupResultPageTpl, err = template.ParseFiles(lookupResultTplFile, navTplFile, footerTplFile)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
- aboutTplFile := t.Cfg.Section("taldir").Key("about_page").MustString(t.getFileName("web/templates/about.html", datahome))
+ aboutTplFile := cfg.Ini.Section("taldir").Key("about_page").MustString(t.getFileName("web/templates/about.html", cfg.Datahome))
t.AboutPageTpl, err = template.ParseFiles(aboutTplFile, navTplFile, footerTplFile)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
t.Salt = os.Getenv("TALDIR_SALT")
- if "" == t.Salt {
- t.Salt = t.Cfg.Section("taldir").Key("salt").MustString("ChangeMe")
+ if t.Salt == "" {
+ t.Salt = cfg.Ini.Section("taldir").Key("salt").MustString("ChangeMe")
}
- t.Host = t.Cfg.Section("taldir").Key("base_url").MustString("http://localhost")
+ t.Host = cfg.Ini.Section("taldir").Key("base_url").MustString("http://localhost")
//merchURL := t.Cfg.Section("taldir").Key("merchant_base_url").MustString("http://merchant.taldir/")
//merchToken := t.Cfg.Section("taldir").Key("merchant_token").MustString("secretAccessToken")
//t.Merchant = merchant.NewMerchant(merchURL, merchToken)