taldir

Directory service to resolve wallet mailboxes by messenger addresses
Log | Files | Refs | Submodules | README | LICENSE

main_test.go (12622B)


      1 // This file is part of taldir, the Taler Directory implementation.
      2 // Copyright (C) 2022 Martin Schanzenbach
      3 //
      4 // Taldir is free software: you can redistribute it and/or modify it
      5 // under the terms of the GNU Affero General Public License as published
      6 // by the Free Software Foundation, either version 3 of the License,
      7 // or (at your option) any later version.
      8 //
      9 // Taldir is distributed in the hope that it will be useful, but
     10 // WITHOUT ANY WARRANTY; without even the implied warranty of
     11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12 // Affero General Public License for more details.
     13 //
     14 // You should have received a copy of the GNU Affero General Public License
     15 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
     16 //
     17 // SPDX-License-Identifier: AGPL3.0-or-later
     18 
     19 package main_test
     20 
     21 import (
     22 	"bytes"
     23 	"database/sql"
     24 	"fmt"
     25 	"io"
     26 	"log"
     27 	"net/http"
     28 	"net/http/httptest"
     29 	"os"
     30 	"strings"
     31 	"testing"
     32 
     33 	"github.com/schanzen/taler-go/pkg/merchant"
     34 	talerutil "github.com/schanzen/taler-go/pkg/util"
     35 	"gopkg.in/ini.v1"
     36 	_ "taler.net/taldir/cmd/taldir-server"
     37 	"taler.net/taldir/internal/util"
     38 	taldir "taler.net/taldir/pkg/taldir"
     39 )
     40 
     41 var t taldir.Taldir
     42 
     43 // Note: This duration will be rounded down to 20 Months (= 51840000000000)
     44 var validRegisterRequest = []byte(`
     45   {
     46     "alias": "abc@test",
     47     "target_uri": "myinbox@xyz",
     48     "duration": 53135000000000
     49   }
     50 `)
     51 
     52 var validRegisterRequestUnmodified = []byte(`
     53   {
     54     "alias": "abc@test",
     55     "target_uri": "myinbox@xyz",
     56     "duration": 0
     57   }
     58 `)
     59 
     60 var newOrderMockResponse = `
     61   {
     62     "order_id": "testOrder1234",
     63     "taler_pay_uri": "payto://ladida"
     64   }
     65 `
     66 
     67 var newOrderStatusUnpaidMockResponse = `
     68   {
     69     "order_status": "unpaid",
     70     "taler_pay_uri": "payto://somedude"
     71   }
     72 `
     73 
     74 const merchantConfigResponse = `{
     75   "currency": "KUDOS",
     76   "currencies": {
     77     "KUDOS": {
     78       "name": "Kudos (Taler Demonstrator)",
     79       "currency": "KUDOS",
     80       "num_fractional_input_digits": 2,
     81       "num_fractional_normal_digits": 2,
     82       "num_fractional_trailing_zero_digits": 2,
     83       "alt_unit_names": {
     84         "0": "ク"
     85       }
     86     }
     87   },
     88   "exchanges": [
     89     {
     90       "master_pub": "F80MFRG8HVH6R9CQ47KRFQSJP3T6DBJ4K1D9B703RJY3Z39TBMJ0",
     91       "currency": "KUDOS",
     92       "base_url": "https://exchange.demo.taler.net/"
     93     }
     94   ],
     95   "implementation": "urn:net:taler:specs:taler-merchant:c-reference",
     96   "name": "taler-merchant",
     97   "version": "18:0:15"
     98 }`
     99 
    100 func TestMain(m *testing.M) {
    101 	cfg, err := ini.LooseLoad("testdata/taldir-test.conf")
    102 	if err != nil {
    103 		log.Fatalf("Failed to read config: %v", err)
    104 	}
    105 	psqlconn := cfg.Section("taldir-pq").Key("connection_string").MustString("postgres:///taler-directory")
    106 	segments := strings.Split(strings.Split(psqlconn, "?")[0], "/")
    107 	dbName := segments[len(segments)-1]
    108 
    109 	db, err := sql.Open("postgres", psqlconn)
    110 	if err != nil {
    111 		log.Panic(err)
    112 	}
    113 	defer db.Close()
    114 	err = talerutil.DBInit(db, "../..", dbName, "taler-directory")
    115 	if err != nil {
    116 		log.Fatalf("Failed to apply versioning or patches: %v", err)
    117 	}
    118 	merchServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    119 		if r.URL.Path == "/config" {
    120 			w.WriteHeader(http.StatusOK)
    121 			w.Write([]byte(merchantConfigResponse))
    122 			return
    123 		}
    124 		if !strings.HasPrefix(r.URL.Path, "/private/orders") {
    125 			log.Printf("Expected to request '/private/orders', got: %s\n", r.URL.Path)
    126 			return
    127 		}
    128 		if r.Method == http.MethodPost {
    129 			jsonResp := fmt.Sprintf("{\"order_id\":\"%s\"}", "uniqueOrderId")
    130 			w.WriteHeader(http.StatusOK)
    131 			w.Write([]byte(jsonResp))
    132 		} else {
    133 			if r.Header.Get("PaidIndicator") == "yes" {
    134 				jsonResp := "{\"order_status\":\"paid\"}"
    135 				w.WriteHeader(http.StatusOK)
    136 				w.Write([]byte(jsonResp))
    137 			} else {
    138 				jsonResp := "{\"order_status\":\"unpaid\", \"taler_pay_uri\": \"somepaytouri\"}"
    139 				w.WriteHeader(http.StatusOK)
    140 				w.Write([]byte(jsonResp))
    141 			}
    142 		}
    143 	}))
    144 	defer merchServer.Close()
    145 	merch := merchant.NewMerchant(merchServer.URL, "supersecret")
    146 	t.Initialize(taldir.TaldirConfig{
    147 		Ini:      cfg,
    148 		Version:  "testing",
    149 		Datahome: "./testdata",
    150 		Db:       db,
    151 		Merchant: merch,
    152 		Loglevel: taldir.LogDebug,
    153 	})
    154 	log.Printf("have %d validators", len(t.Validators))
    155 	log.Print(t.Validators)
    156 	code := m.Run()
    157 	t.ClearDatabase()
    158 	os.Exit(code)
    159 }
    160 
    161 func getHAlias(alias string) string {
    162 	ha := taldir.HashAlias("test", alias)
    163 	return util.Base32CrockfordEncode(ha)
    164 }
    165 
    166 func TestNoEntry(s *testing.T) {
    167 	t.ClearDatabase()
    168 
    169 	hAlias := getHAlias("jdoe@example.com")
    170 	req, _ := http.NewRequest("GET", "/"+hAlias, nil)
    171 	response := executeRequest(req)
    172 
    173 	if http.StatusNotFound != response.Code {
    174 		s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound, response.Code)
    175 	}
    176 }
    177 
    178 func executeRequest(req *http.Request) *httptest.ResponseRecorder {
    179 	rr := httptest.NewRecorder()
    180 	t.Router.ServeHTTP(rr, req)
    181 	return rr
    182 }
    183 
    184 func TestRegisterRequest(s *testing.T) {
    185 	t.ClearDatabase()
    186 
    187 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    188 	response := executeRequest(req)
    189 
    190 	if http.StatusAccepted != response.Code {
    191 		s.Errorf("Expected response code %d. Got %d, with %s\n", http.StatusAccepted, response.Code, response.Body)
    192 	}
    193 	file, err := os.Open("validation_code")
    194 	if err != nil {
    195 		s.Errorf("No validation code file found!\n")
    196 	}
    197 	code, err := io.ReadAll(file)
    198 	if err != nil {
    199 		s.Errorf("Error reading validation code file contents!\n")
    200 	}
    201 	hAlias := getHAlias("abc@test")
    202 	trimCode := strings.Trim(string(code), " \r\n")
    203 	solution := util.GenerateSolution("myinbox@xyz", trimCode)
    204 	solutionJSON := "{\"solution\": \"" + solution + "\"}"
    205 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    206 	response = executeRequest(req)
    207 	if http.StatusNoContent != response.Code {
    208 		s.Errorf("Expected response code %d. Got %d\n", http.StatusNoContent, response.Code)
    209 	}
    210 }
    211 
    212 func TestRegisterQRPageRequest(s *testing.T) {
    213 	t.ClearDatabase()
    214 
    215 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    216 	response := executeRequest(req)
    217 
    218 	if http.StatusAccepted != response.Code {
    219 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    220 	}
    221 	req, _ = http.NewRequest("GET", "/register/NonSenseAddr/NonSenseCode", nil)
    222 	response = executeRequest(req)
    223 	if http.StatusNotFound != response.Code {
    224 		s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound, response.Code)
    225 	}
    226 
    227 	file, err := os.Open("validation_code")
    228 	if err != nil {
    229 		s.Errorf("No validation code file found!\n")
    230 	}
    231 	code, err := io.ReadAll(file)
    232 	if err != nil {
    233 		s.Errorf("Error reading validation code file contents!\n")
    234 	}
    235 	hAlias := getHAlias("abc@test")
    236 	trimCode := strings.Trim(string(code), " \r\n")
    237 	req, _ = http.NewRequest("GET", "/register/"+hAlias+"/"+trimCode+"?alias="+"abc@test", nil)
    238 	response = executeRequest(req)
    239 	if http.StatusOK != response.Code {
    240 		s.Errorf("Expected response code %d. Got %d\n", http.StatusOK, response.Code)
    241 	}
    242 }
    243 
    244 func TestReRegisterRequest(s *testing.T) {
    245 	t.ClearDatabase()
    246 
    247 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    248 	response := executeRequest(req)
    249 
    250 	if http.StatusAccepted != response.Code {
    251 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    252 	}
    253 	file, err := os.Open("validation_code")
    254 	if err != nil {
    255 		s.Errorf("No validation code file found!\n")
    256 	}
    257 	code, err := io.ReadAll(file)
    258 	if err != nil {
    259 		s.Errorf("Error reading validation code file contents!\n")
    260 	}
    261 	hAlias := getHAlias("abc@test")
    262 	trimCode := strings.Trim(string(code), " \r\n")
    263 	solution := util.GenerateSolution("myinbox@xyz", trimCode)
    264 	solutionJSON := "{\"solution\": \"" + solution + "\"}"
    265 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    266 	response = executeRequest(req)
    267 	if http.StatusNoContent != response.Code {
    268 		s.Errorf("Expected response code %d. Got %d\n", http.StatusNoContent, response.Code)
    269 	}
    270 	req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequestUnmodified))
    271 	response = executeRequest(req)
    272 
    273 	if http.StatusOK != response.Code {
    274 		s.Errorf("Expected response code %d. Got %d\n", http.StatusOK, response.Code)
    275 	}
    276 
    277 }
    278 
    279 func TestReRegisterRequestTooMany(s *testing.T) {
    280 	t.ClearDatabase()
    281 
    282 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    283 	response := executeRequest(req)
    284 
    285 	if http.StatusAccepted != response.Code {
    286 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    287 	}
    288 	req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    289 	response = executeRequest(req)
    290 
    291 	if http.StatusAccepted != response.Code {
    292 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    293 	}
    294 	req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    295 	response = executeRequest(req)
    296 
    297 	if http.StatusAccepted != response.Code {
    298 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    299 	}
    300 	req, _ = http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    301 	response = executeRequest(req)
    302 
    303 	if http.StatusTooManyRequests != response.Code {
    304 		s.Errorf("Expected response code %d. Got %d\n", http.StatusTooManyRequests, response.Code)
    305 	}
    306 
    307 }
    308 
    309 func TestSolutionRequestTooMany(s *testing.T) {
    310 	t.ClearDatabase()
    311 
    312 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    313 	response := executeRequest(req)
    314 
    315 	if http.StatusAccepted != response.Code {
    316 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    317 	}
    318 	hAlias := getHAlias("abc@test")
    319 	solution := util.GenerateSolution("myinbox@xyz", "wrongSolution")
    320 	solutionJSON := "{\"solution\": \"" + solution + "\"}"
    321 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    322 	response = executeRequest(req)
    323 	if http.StatusForbidden != response.Code {
    324 		s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden, response.Code)
    325 	}
    326 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    327 	response = executeRequest(req)
    328 	if http.StatusForbidden != response.Code {
    329 		s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden, response.Code)
    330 	}
    331 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    332 	response = executeRequest(req)
    333 	if http.StatusForbidden != response.Code {
    334 		s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden, response.Code)
    335 	}
    336 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    337 	response = executeRequest(req)
    338 	if http.StatusTooManyRequests != response.Code {
    339 		s.Errorf("Expected response code %d. Got %d\n", http.StatusTooManyRequests, response.Code)
    340 	}
    341 
    342 }
    343 
    344 func TestRegisterRequestWrongTargetUri(s *testing.T) {
    345 	t.ClearDatabase()
    346 
    347 	req, _ := http.NewRequest("POST", "/register/test", bytes.NewBuffer(validRegisterRequest))
    348 	response := executeRequest(req)
    349 
    350 	if http.StatusAccepted != response.Code {
    351 		s.Errorf("Expected response code %d. Got %d\n", http.StatusAccepted, response.Code)
    352 	}
    353 	file, err := os.Open("validation_code")
    354 	if err != nil {
    355 		s.Errorf("No validation code file found!\n")
    356 	}
    357 	code, err := io.ReadAll(file)
    358 	if err != nil {
    359 		s.Errorf("Error reading validation code file contents!\n")
    360 	}
    361 	hAlias := getHAlias("abc@test")
    362 	trimCode := strings.Trim(string(code), " \r\n")
    363 	solution := util.GenerateSolution("myinox@xyz", trimCode)
    364 	solutionJSON := "{\"solution\": \"" + solution + "\"}"
    365 	req, _ = http.NewRequest("POST", "/"+hAlias, bytes.NewBuffer([]byte(solutionJSON)))
    366 	response = executeRequest(req)
    367 	if http.StatusForbidden != response.Code {
    368 		s.Errorf("Expected response code %d. Got %d\n", http.StatusForbidden, response.Code)
    369 	}
    370 }
    371 
    372 func TestUnsupportedAliasType(s *testing.T) {
    373 	t.ClearDatabase()
    374 
    375 	req, _ := http.NewRequest("POST", "/register/email", bytes.NewBuffer(validRegisterRequest))
    376 	response := executeRequest(req)
    377 
    378 	if http.StatusNotFound != response.Code {
    379 		s.Errorf("Expected response code %d. Got %d\n", http.StatusNotFound, response.Code)
    380 	}
    381 }
    382 
    383 func TestPaymentRequiredMethod(s *testing.T) {
    384 	t.ClearDatabase()
    385 	t.MonthlyFee = "KUDOS:5"
    386 	req, _ := http.NewRequest("POST", "/register/test-cost", bytes.NewBuffer(validRegisterRequest))
    387 
    388 	response := executeRequest(req)
    389 	t.MonthlyFee = "KUDOS:0"
    390 	if http.StatusPaymentRequired != response.Code {
    391 		s.Errorf("Expected response code %d. Got %d\n", http.StatusPaymentRequired, response.Code)
    392 	}
    393 }