taldir

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

main_test.go (12393B)


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