How to use del method of tdhttp Package

Best Go-testdeep code snippet using tdhttp.del

test_api.go

Source:test_api.go Github

copy

Full Screen

...308//309// See [NewRequest] for all possible formats accepted in headersQueryParams.310func (ta *TestAPI) Delete(target string, body io.Reader, headersQueryParams ...any) *TestAPI {311 ta.t.Helper()312 req, err := del(target, body, headersQueryParams...)313 if err != nil {314 ta.t.Fatal(err)315 }316 return ta.Request(req)317}318// NewJSONRequest sends a HTTP request with body marshaled to319// JSON. "Content-Type" header is automatically set to320// "application/json". Any Cmp* or [TestAPI.NoBody] methods can now be called.321//322// Note that [TestAPI.Failed] status is reset just after this call.323//324// See [NewRequest] for all possible formats accepted in headersQueryParams.325func (ta *TestAPI) NewJSONRequest(method, target string, body any, headersQueryParams ...any) *TestAPI {326 ta.t.Helper()327 req, err := newJSONRequest(method, target, body, headersQueryParams...)328 if err != nil {329 ta.t.Fatal(err)330 }331 return ta.Request(req)332}333// PostJSON sends a HTTP POST with body marshaled to334// JSON. "Content-Type" header is automatically set to335// "application/json". Any Cmp* or [TestAPI.NoBody] methods can now be called.336//337// Note that [TestAPI.Failed] status is reset just after this call.338//339// See [NewRequest] for all possible formats accepted in headersQueryParams.340func (ta *TestAPI) PostJSON(target string, body any, headersQueryParams ...any) *TestAPI {341 ta.t.Helper()342 req, err := newJSONRequest(http.MethodPost, target, body, headersQueryParams...)343 if err != nil {344 ta.t.Fatal(err)345 }346 return ta.Request(req)347}348// PutJSON sends a HTTP PUT with body marshaled to349// JSON. "Content-Type" header is automatically set to350// "application/json". Any Cmp* or [TestAPI.NoBody] methods can now be called.351//352// Note that [TestAPI.Failed] status is reset just after this call.353//354// See [NewRequest] for all possible formats accepted in headersQueryParams.355func (ta *TestAPI) PutJSON(target string, body any, headersQueryParams ...any) *TestAPI {356 ta.t.Helper()357 req, err := newJSONRequest(http.MethodPut, target, body, headersQueryParams...)358 if err != nil {359 ta.t.Fatal(err)360 }361 return ta.Request(req)362}363// PatchJSON sends a HTTP PATCH with body marshaled to364// JSON. "Content-Type" header is automatically set to365// "application/json". Any Cmp* or [TestAPI.NoBody] methods can now be called.366//367// Note that [TestAPI.Failed] status is reset just after this call.368//369// See [NewRequest] for all possible formats accepted in headersQueryParams.370func (ta *TestAPI) PatchJSON(target string, body any, headersQueryParams ...any) *TestAPI {371 ta.t.Helper()372 req, err := newJSONRequest(http.MethodPatch, target, body, headersQueryParams...)373 if err != nil {374 ta.t.Fatal(err)375 }376 return ta.Request(req)377}378// DeleteJSON sends a HTTP DELETE with body marshaled to379// JSON. "Content-Type" header is automatically set to380// "application/json". Any Cmp* or [TestAPI.NoBody] methods can now be called.381//382// Note that [TestAPI.Failed] status is reset just after this call.383//384// See [NewRequest] for all possible formats accepted in headersQueryParams.385func (ta *TestAPI) DeleteJSON(target string, body any, headersQueryParams ...any) *TestAPI {386 ta.t.Helper()387 req, err := newJSONRequest(http.MethodDelete, target, body, headersQueryParams...)388 if err != nil {389 ta.t.Fatal(err)390 }391 return ta.Request(req)392}393// NewXMLRequest sends a HTTP request with body marshaled to394// XML. "Content-Type" header is automatically set to395// "application/xml". Any Cmp* or [TestAPI.NoBody] methods can now be called.396//397// Note that [TestAPI.Failed] status is reset just after this call.398//399// See [NewRequest] for all possible formats accepted in headersQueryParams.400func (ta *TestAPI) NewXMLRequest(method, target string, body any, headersQueryParams ...any) *TestAPI {401 ta.t.Helper()402 req, err := newXMLRequest(method, target, body, headersQueryParams...)403 if err != nil {404 ta.t.Fatal(err)405 }406 return ta.Request(req)407}408// PostXML sends a HTTP POST with body marshaled to409// XML. "Content-Type" header is automatically set to410// "application/xml". Any Cmp* or [TestAPI.NoBody] methods can now be called.411//412// Note that [TestAPI.Failed] status is reset just after this call.413//414// See [NewRequest] for all possible formats accepted in headersQueryParams.415func (ta *TestAPI) PostXML(target string, body any, headersQueryParams ...any) *TestAPI {416 ta.t.Helper()417 req, err := newXMLRequest(http.MethodPost, target, body, headersQueryParams...)418 if err != nil {419 ta.t.Fatal(err)420 }421 return ta.Request(req)422}423// PutXML sends a HTTP PUT with body marshaled to424// XML. "Content-Type" header is automatically set to425// "application/xml". Any Cmp* or [TestAPI.NoBody] methods can now be called.426//427// Note that [TestAPI.Failed] status is reset just after this call.428//429// See [NewRequest] for all possible formats accepted in headersQueryParams.430func (ta *TestAPI) PutXML(target string, body any, headersQueryParams ...any) *TestAPI {431 ta.t.Helper()432 req, err := newXMLRequest(http.MethodPut, target, body, headersQueryParams...)433 if err != nil {434 ta.t.Fatal(err)435 }436 return ta.Request(req)437}438// PatchXML sends a HTTP PATCH with body marshaled to439// XML. "Content-Type" header is automatically set to440// "application/xml". Any Cmp* or [TestAPI.NoBody] methods can now be called.441//442// Note that [TestAPI.Failed] status is reset just after this call.443//444// See [NewRequest] for all possible formats accepted in headersQueryParams.445func (ta *TestAPI) PatchXML(target string, body any, headersQueryParams ...any) *TestAPI {446 ta.t.Helper()447 req, err := newXMLRequest(http.MethodPatch, target, body, headersQueryParams...)448 if err != nil {449 ta.t.Fatal(err)450 }451 return ta.Request(req)452}453// DeleteXML sends a HTTP DELETE with body marshaled to454// XML. "Content-Type" header is automatically set to455// "application/xml". Any Cmp* or [TestAPI.NoBody] methods can now be called.456//457// Note that [TestAPI.Failed] status is reset just after this call.458//459// See [NewRequest] for all possible formats accepted in headersQueryParams.460func (ta *TestAPI) DeleteXML(target string, body any, headersQueryParams ...any) *TestAPI {461 ta.t.Helper()462 req, err := newXMLRequest(http.MethodDelete, target, body, headersQueryParams...)463 if err != nil {464 ta.t.Fatal(err)465 }466 return ta.Request(req)467}468// CmpResponse tests the last request response status against469// expectedResponse. expectedResponse can be a *http.Response or more470// probably a [td.TestDeep] operator.471//472// ta := tdhttp.NewTestAPI(t, mux)473//474// ta.Get("/test").475// CmpResponse(td.Struct(476// &http.Response{Status: http.StatusOK}, td.StructFields{477// "Header": td.SuperMapOf(http.Header{"X-Test": {"pipo"}}),478// "ContentLength": td.Gt(10),479// }))480//481// Some tests can be hard to achieve using operators chaining. In this482// case, the [td.Code] operator can be used to take the full control483// over the extractions and comparisons to do:484//485// ta.Get("/test").486// CmpResponse(td.Code(func (assert, require *td.T, r *http.Response) {487// token, err := ParseToken(r.Header.Get("X-Token"))488// require.CmpNoError(err)489//490// baseURL,err := url.Parse(r.Header.Get("X-Base-URL"))491// require.CmpNoError(err)492//493// assert.Cmp(baseURL.Query().Get("id"), token.ID)494// }))495//496// It fails if no request has been sent yet.497func (ta *TestAPI) CmpResponse(expectedResponse any) *TestAPI {498 defer ta.t.AnchorsPersistTemporarily()()499 ta.t.Helper()500 if !ta.checkRequestSent() {501 ta.failed |= responseFailed502 return ta503 }504 if !ta.t.RootName("Response").505 Cmp(ta.response.Result(), expectedResponse, ta.name+"full response should match") {506 ta.failed |= responseFailed507 if ta.autoDumpResponse {508 ta.dumpResponse()509 }510 }511 return ta512}513// CmpStatus tests the last request response status against514// expectedStatus. expectedStatus can be an int to match a fixed HTTP515// status code, or a [td.TestDeep] operator.516//517// ta := tdhttp.NewTestAPI(t, mux)518//519// ta.Get("/test").520// CmpStatus(http.StatusOK)521//522// ta.PostJSON("/new", map[string]string{"name": "Bob"}).523// CmpStatus(td.Between(200, 202))524//525// It fails if no request has been sent yet.526func (ta *TestAPI) CmpStatus(expectedStatus any) *TestAPI {527 defer ta.t.AnchorsPersistTemporarily()()528 ta.t.Helper()529 if !ta.checkRequestSent() {530 ta.failed |= statusFailed531 return ta532 }533 if !ta.t.RootName("Response.Status").534 CmpLax(ta.response.Code, expectedStatus, ta.name+"status code should match") {535 ta.failed |= statusFailed536 if ta.autoDumpResponse {537 ta.dumpResponse()538 }539 }540 return ta541}542// CmpHeader tests the last request response header against543// expectedHeader. expectedHeader can be a [http.Header] or a544// [td.TestDeep] operator. Keep in mind that if it is a [http.Header],545// it has to match exactly the response header. Often only the546// presence of a header key is needed:547//548// ta := tdhttp.NewTestAPI(t, mux).549// PostJSON("/new", map[string]string{"name": "Bob"}).550// CmdStatus(201).551// CmpHeader(td.ContainsKey("X-Custom"))552//553// or some specific key, value pairs:554//555// ta.CmpHeader(td.SuperMapOf(556// http.Header{557// "X-Account": []string{"Bob"},558// },559// td.MapEntries{560// "X-Token": td.Bag(td.Re(`^[a-z0-9-]{32}\z`)),561// }),562// )563//564// Note that CmpHeader calls can be chained:565//566// ta.CmpHeader(td.ContainsKey("X-Account")).567// CmpHeader(td.ContainsKey("X-Token"))568//569// instead of doing all tests in one call as [td.All] operator allows it:570//571// ta.CmpHeader(td.All(572// td.ContainsKey("X-Account"),573// td.ContainsKey("X-Token"),574// ))575//576// It fails if no request has been sent yet.577func (ta *TestAPI) CmpHeader(expectedHeader any) *TestAPI {578 defer ta.t.AnchorsPersistTemporarily()()579 ta.t.Helper()580 if !ta.checkRequestSent() {581 ta.failed |= headerFailed582 return ta583 }584 if !ta.t.RootName("Response.Header").585 CmpLax(ta.response.Result().Header, expectedHeader, ta.name+"header should match") {586 ta.failed |= headerFailed587 if ta.autoDumpResponse {588 ta.dumpResponse()589 }590 }591 return ta592}593// CmpTrailer tests the last request response trailer against594// expectedTrailer. expectedTrailer can be a [http.Header] or a595// [td.TestDeep] operator. Keep in mind that if it is a [http.Header],596// it has to match exactly the response trailer. Often only the597// presence of a trailer key is needed:598//599// ta := tdhttp.NewTestAPI(t, mux).600// PostJSON("/new", map[string]string{"name": "Bob"}).601// CmdStatus(201).602// CmpTrailer(td.ContainsKey("X-Custom"))603//604// or some specific key, value pairs:605//606// ta.CmpTrailer(td.SuperMapOf(607// http.Header{608// "X-Account": []string{"Bob"},609// },610// td.MapEntries{611// "X-Token": td.Re(`^[a-z0-9-]{32}\z`),612// }),613// )614//615// Note that CmpTrailer calls can be chained:616//617// ta.CmpTrailer(td.ContainsKey("X-Account")).618// CmpTrailer(td.ContainsKey("X-Token"))619//620// instead of doing all tests in one call as [td.All] operator allows it:621//622// ta.CmpTrailer(td.All(623// td.ContainsKey("X-Account"),624// td.ContainsKey("X-Token"),625// ))626//627// It fails if no request has been sent yet.628//629// Note that until go1.19, it does not handle multiple values in630// a single Trailer header field.631func (ta *TestAPI) CmpTrailer(expectedTrailer any) *TestAPI {632 defer ta.t.AnchorsPersistTemporarily()()633 ta.t.Helper()634 if !ta.checkRequestSent() {635 ta.failed |= trailerFailed636 return ta637 }638 if !ta.t.RootName("Response.Trailer").639 CmpLax(ta.response.Result().Trailer, expectedTrailer, ta.name+"trailer should match") {640 ta.failed |= trailerFailed641 if ta.autoDumpResponse {642 ta.dumpResponse()643 }644 }645 return ta646}647// CmpCookies tests the last request response cookies against648// expectedCookies. expectedCookies can be a [][*http.Cookie] or a649// [td.TestDeep] operator. Keep in mind that if it is a650// [][*http.Cookie], it has to match exactly the response651// cookies. Often only the presence of a cookie key is needed:652//653// ta := tdhttp.NewTestAPI(t, mux).654// PostJSON("/login", map[string]string{"name": "Bob", "password": "Sponge"}).655// CmdStatus(200).656// CmpCookies(td.SuperBagOf(td.Struct(&http.Cookie{Name: "cookie_session"}, nil))).657// CmpCookies(td.SuperBagOf(td.Smuggle("Name", "cookie_session"))) // shorter658//659// To make tests easier, [http.Cookie.Raw] and [http.Cookie.RawExpires] fields660// of each [*http.Cookie] are zeroed before doing the comparison. So no need661// to fill them when comparing against a simple literal as in:662//663// ta := tdhttp.NewTestAPI(t, mux).664// PostJSON("/login", map[string]string{"name": "Bob", "password": "Sponge"}).665// CmdStatus(200).666// CmpCookies([]*http.Cookies{667// {Name: "cookieName1", Value: "cookieValue1"},668// {Name: "cookieName2", Value: "cookieValue2"},669// })670//671// It fails if no request has been sent yet.672func (ta *TestAPI) CmpCookies(expectedCookies any) *TestAPI {673 defer ta.t.AnchorsPersistTemporarily()()674 ta.t.Helper()675 if !ta.checkRequestSent() {676 ta.failed |= cookiesFailed677 return ta678 }679 // Empty Raw* fields to make comparisons easier680 cookies := ta.response.Result().Cookies()681 for _, c := range cookies {682 c.RawExpires, c.Raw = "", ""683 }684 if !ta.t.RootName("Response.Cookie").685 CmpLax(cookies, expectedCookies, ta.name+"cookies should match") {686 ta.failed |= cookiesFailed687 if ta.autoDumpResponse {688 ta.dumpResponse()689 }690 }691 return ta692}693// findCmpXBodyCaller finds the oldest Cmp* method called.694func findCmpXBodyCaller() string {695 var (696 fn string697 pc [20]uintptr698 found bool699 )700 if num := runtime.Callers(5, pc[:]); num > 0 {701 frames := runtime.CallersFrames(pc[:num])702 for {703 frame, more := frames.Next()704 if pos := strings.Index(frame.Function, "tdhttp.(*TestAPI).Cmp"); pos > 0 {705 fn = frame.Function[pos+18:]706 found = true707 } else if found {708 more = false709 }710 if !more {711 break712 }713 }714 }715 return fn716}717func (ta *TestAPI) cmpMarshaledBody(718 acceptEmptyBody bool,719 unmarshal func([]byte, any) error,720 expectedBody any,721) *TestAPI {722 defer ta.t.AnchorsPersistTemporarily()()723 ta.t.Helper()724 if !ta.checkRequestSent() {725 ta.failed |= bodyFailed726 return ta727 }728 if !acceptEmptyBody &&729 !ta.t.RootName("Response body").Code(ta.response.Body.Bytes(),730 func(b []byte) error {731 if len(b) > 0 {732 return nil733 }734 return &ctxerr.Error{735 Message: "%% is empty!",736 Summary: ctxerr.NewSummary(737 "Body cannot be empty when using " + findCmpXBodyCaller()),738 }739 },740 ta.name+"body should not be empty") {741 ta.failed |= bodyFailed742 if ta.autoDumpResponse {743 ta.dumpResponse()744 }745 return ta746 }747 tt := ta.t.RootName("Response.Body")748 var bodyType reflect.Type749 // If expectedBody is a TestDeep operator, try to ask it the type750 // behind it. It should work in most cases (typically Struct(),751 // Map() & Slice()).752 var unknownExpectedType, showRawBody bool753 op, ok := expectedBody.(td.TestDeep)754 if ok {755 bodyType = op.TypeBehind()756 if bodyType == nil {757 // As the expected body type cannot be guessed, try to758 // unmarshal in an any759 bodyType = types.Interface760 unknownExpectedType = true761 // Special case for Ignore & NotEmpty operators762 switch op.GetLocation().Func {763 case "Ignore", "NotEmpty":764 showRawBody = (ta.failed & statusFailed) != 0 // Show real body if status failed765 }766 }767 } else {768 bodyType = reflect.TypeOf(expectedBody)769 if bodyType == nil {770 bodyType = types.Interface771 }772 }773 // For unmarshaling below, body must be a pointer774 bodyPtr := reflect.New(bodyType)775 // Try to unmarshal body776 if !tt.RootName("unmarshal(Response.Body)").777 CmpNoError(unmarshal(ta.response.Body.Bytes(), bodyPtr.Interface()), ta.name+"body unmarshaling") {778 // If unmarshal failed, perhaps it's coz the expected body type779 // is unknown?780 if unknownExpectedType {781 tt.Logf("Cannot guess the body expected type as %[1]s TestDeep\n"+782 "operator does not know the type behind it.\n"+783 "You can try All(Isa(EXPECTED_TYPE), %[1]s(…)) to disambiguate…",784 op.GetLocation().Func)785 }786 showRawBody = true // let's show its real body contents787 ta.failed |= bodyFailed788 } else if !tt.Cmp(bodyPtr.Elem().Interface(), expectedBody, ta.name+"body contents is OK") {789 // Try to catch bad body expected type when nothing has been set790 // to non-zero during unmarshaling body. In this case, require791 // to show raw body contents.792 if len(ta.response.Body.Bytes()) > 0 &&793 td.EqDeeply(bodyPtr.Interface(), reflect.New(bodyType).Interface()) {794 showRawBody = true795 tt.Log("Hmm… It seems nothing has been set during unmarshaling…")796 }797 ta.failed |= bodyFailed798 }799 if showRawBody || ((ta.failed&bodyFailed) != 0 && ta.autoDumpResponse) {800 ta.dumpResponse()801 }802 return ta803}804// CmpMarshaledBody tests that the last request response body can be805// unmarshaled using unmarshal function and then, that it matches806// expectedBody. expectedBody can be any type unmarshal function can807// handle, or a [td.TestDeep] operator.808//809// See [TestAPI.CmpJSONBody] and [TestAPI.CmpXMLBody] sources for810// examples of use.811//812// It fails if no request has been sent yet.813func (ta *TestAPI) CmpMarshaledBody(unmarshal func([]byte, any) error, expectedBody any) *TestAPI {814 ta.t.Helper()815 return ta.cmpMarshaledBody(false, unmarshal, expectedBody)816}817// CmpBody tests the last request response body against818// expectedBody. expectedBody can be a []byte, a string or a819// [td.TestDeep] operator.820//821// ta := tdhttp.NewTestAPI(t, mux)822//823// ta.Get("/test").824// CmpStatus(http.StatusOK).825// CmpBody("OK!\n")826//827// ta.Get("/test").828// CmpStatus(http.StatusOK).829// CmpBody(td.Contains("OK"))830//831// It fails if no request has been sent yet.832func (ta *TestAPI) CmpBody(expectedBody any) *TestAPI {833 ta.t.Helper()834 if expectedBody == nil {835 return ta.NoBody()836 }837 return ta.cmpMarshaledBody(838 true, // accept empty body839 func(body []byte, target any) error {840 switch target := target.(type) {841 case *string:842 *target = string(body)843 case *[]byte:844 *target = body845 case *any:846 *target = body847 default:848 // cmpMarshaledBody always calls us with target as a pointer849 return fmt.Errorf(850 "CmpBody only accepts expectedBody be a []byte, a string or a TestDeep operator allowing to match these types, but not type %s",851 reflect.TypeOf(target).Elem())852 }853 return nil854 },855 expectedBody)856}857// CmpJSONBody tests that the last request response body can be858// [json.Unmarshal]'ed and that it matches expectedBody. expectedBody859// can be any type one can [json.Unmarshal] into, or a [td.TestDeep]860// operator.861//862// ta := tdhttp.NewTestAPI(t, mux)863//864// ta.Get("/person/42").865// CmpStatus(http.StatusOK).866// CmpJSONBody(Person{867// ID: 42,868// Name: "Bob",869// Age: 26,870// })871//872// ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).873// CmpStatus(http.StatusCreated).874// CmpJSONBody(td.SStruct(875// Person{876// Name: "Bob",877// Age: 26,878// },879// td.StructFields{880// "ID": td.NotZero(),881// }))882//883// The same with anchoring, and so without [td.SStruct]:884//885// ta := tdhttp.NewTestAPI(tt, mux)886//887// ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).888// CmpStatus(http.StatusCreated).889// CmpJSONBody(Person{890// ID: ta.Anchor(td.NotZero(), uint64(0)).(uint64),891// Name: "Bob",892// Age: 26,893// })894//895// The same using [td.JSON]:896//897// ta.PostJSON("/person", Person{Name: "Bob", Age: 23}).898// CmpStatus(http.StatusCreated).899// CmpJSONBody(td.JSON(`900// {901// "id": NotZero(),902// "name": "Bob",903// "age": 26904// }`))905//906// It fails if no request has been sent yet.907func (ta *TestAPI) CmpJSONBody(expectedBody any) *TestAPI {908 ta.t.Helper()909 return ta.CmpMarshaledBody(json.Unmarshal, expectedBody)910}911// CmpXMLBody tests that the last request response body can be912// [xml.Unmarshal]'ed and that it matches expectedBody. expectedBody913// can be any type one can [xml.Unmarshal] into, or a [td.TestDeep]914// operator.915//916// ta := tdhttp.NewTestAPI(t, mux)917//918// ta.Get("/person/42").919// CmpStatus(http.StatusOK).920// CmpXMLBody(Person{921// ID: 42,922// Name: "Bob",923// Age: 26,924// })925//926// ta.Get("/person/43").927// CmpStatus(http.StatusOK).928// CmpXMLBody(td.SStruct(929// Person{930// Name: "Bob",931// Age: 26,932// },933// td.StructFields{934// "ID": td.NotZero(),935// }))936//937// The same with anchoring:938//939// ta := tdhttp.NewTestAPI(tt, mux)940//941// ta.Get("/person/42").942// CmpStatus(http.StatusOK).943// CmpXMLBody(Person{944// ID: ta.Anchor(td.NotZero(), uint64(0)).(uint64),945// Name: "Bob",946// Age: 26,947// })948//949// It fails if no request has been sent yet.950func (ta *TestAPI) CmpXMLBody(expectedBody any) *TestAPI {951 ta.t.Helper()952 return ta.CmpMarshaledBody(xml.Unmarshal, expectedBody)953}954// NoBody tests that the last request response body is empty.955//956// It fails if no request has been sent yet.957func (ta *TestAPI) NoBody() *TestAPI {958 defer ta.t.AnchorsPersistTemporarily()()959 ta.t.Helper()960 if !ta.checkRequestSent() {961 ta.failed |= bodyFailed962 return ta963 }964 ok := ta.t.RootName("Response.Body").965 Code(len(ta.response.Body.Bytes()) == 0,966 func(empty bool) error {967 if empty {968 return nil969 }970 return &ctxerr.Error{971 Message: "%% is not empty",972 Got: types.RawString("not empty"),973 Expected: types.RawString("empty"),974 }975 },976 "body should be empty")977 if !ok {978 ta.failed |= bodyFailed979 // Systematically dump response, no AutoDumpResponse needed980 ta.dumpResponse()981 }982 return ta983}984// Or executes function fn if ta.Failed() is true at the moment it is called.985//986// fn can have several types:987// - func(body string) or func(t *td.T, body string)988// → fn is called with response body as a string.989// If no response has been received yet, body is "";990// - func(body []byte) or func(t *td.T, body []byte)991// → fn is called with response body as a []byte.992// If no response has been received yet, body is nil;993// - func(t *td.T, resp *httptest.ResponseRecorder)994// → fn is called with the internal object containing the response.995// See net/http/httptest for details.996// If no response has been received yet, resp is nil.997//998// If fn type is not one of these types, it calls ta.T().Fatal().999func (ta *TestAPI) Or(fn any) *TestAPI {1000 ta.t.Helper()1001 switch fn := fn.(type) {1002 case func(string):1003 if ta.Failed() {1004 var body string1005 if ta.response != nil && ta.response.Body != nil {1006 body = ta.response.Body.String()1007 }1008 fn(body)1009 }1010 case func(*td.T, string):1011 if ta.Failed() {1012 var body string1013 if ta.response != nil && ta.response.Body != nil {1014 body = ta.response.Body.String()1015 }1016 fn(ta.t, body)1017 }1018 case func([]byte):1019 if ta.Failed() {1020 var body []byte1021 if ta.response != nil && ta.response.Body != nil {1022 body = ta.response.Body.Bytes()1023 }1024 fn(body)1025 }1026 case func(*td.T, []byte):1027 if ta.Failed() {1028 var body []byte1029 if ta.response != nil && ta.response.Body != nil {1030 body = ta.response.Body.Bytes()1031 }1032 fn(ta.t, body)1033 }1034 case func(*td.T, *httptest.ResponseRecorder):1035 if ta.Failed() {1036 fn(ta.t, ta.response)1037 }1038 default:1039 ta.t.Fatal(color.BadUsage(1040 "Or(func([*td.T,]string) | func([*td.T,][]byte) | func(*td.T,*httptest.ResponseRecorder))",1041 fn, 1, true))1042 }1043 return ta1044}1045// OrDumpResponse dumps the response if at least one previous test failed.1046//1047// ta := tdhttp.NewTestAPI(t, handler)1048//1049// ta.Get("/foo").1050// CmpStatus(200).1051// OrDumpResponse(). // if status check failed, dumps the response1052// CmpBody("bar") // if it fails, the response is not dumped1053//1054// ta.Get("/foo").1055// CmpStatus(200).1056// CmpBody("bar").1057// OrDumpResponse() // dumps the response if status and/or body checks fail1058//1059// See [TestAPI.AutoDumpResponse] method to automatize this dump.1060func (ta *TestAPI) OrDumpResponse() *TestAPI {1061 if ta.Failed() {1062 ta.dumpResponse()1063 }1064 return ta1065}1066func (ta *TestAPI) dumpResponse() {1067 if ta.responseDumped {1068 return1069 }1070 ta.t.Helper()1071 if ta.response != nil {1072 ta.responseDumped = true1073 internal.DumpResponse(ta.t, ta.response.Result())1074 return1075 }1076 ta.t.Logf("No response received yet")1077}1078// Anchor returns a typed value allowing to anchor the [td.TestDeep]1079// operator operator in a go classic literal like a struct, slice,1080// array or map value.1081//1082// ta := tdhttp.NewTestAPI(tt, mux)1083//1084// ta.Get("/person/42").1085// CmpStatus(http.StatusOK).1086// CmpJSONBody(Person{1087// ID: ta.Anchor(td.NotZero(), uint64(0)).(uint64),1088// Name: "Bob",1089// Age: 26,1090// })1091//1092// See [td.T.Anchor] for details.1093//1094// See [TestAPI.A] method for a shorter synonym of Anchor.1095func (ta *TestAPI) Anchor(operator td.TestDeep, model ...any) any {1096 return ta.t.Anchor(operator, model...)1097}1098// A is a synonym for [TestAPI.Anchor]. It returns a typed value allowing to1099// anchor the [td.TestDeep] operator in a go classic literal1100// like a struct, slice, array or map value.1101//1102// ta := tdhttp.NewTestAPI(tt, mux)1103//1104// ta.Get("/person/42").1105// CmpStatus(http.StatusOK).1106// CmpJSONBody(Person{1107// ID: ta.A(td.NotZero(), uint64(0)).(uint64),1108// Name: "Bob",1109// Age: 26,1110// })1111//1112// See [td.T.Anchor] for details.1113func (ta *TestAPI) A(operator td.TestDeep, model ...any) any {1114 return ta.Anchor(operator, model...)1115}1116// SentAt returns the time just before the last request is handled. It1117// can be used to check the time a route sets and returns, as in:1118//1119// ta.PostJSON("/person/42", Person{Name: "Bob", Age: 23}).1120// CmpStatus(http.StatusCreated).1121// CmpJSONBody(Person{1122// ID: ta.A(td.NotZero(), uint64(0)).(uint64),1123// Name: "Bob",1124// Age: 23,1125// CreatedAt: ta.A(td.Between(ta.SentAt(), time.Now())).(time.Time),1126// })1127//1128// checks that CreatedAt field is included between the time when the...

Full Screen

Full Screen

request.go

Source:request.go Github

copy

Full Screen

...140}141func patch(target string, body io.Reader, headersQueryParams ...any) (*http.Request, error) {142 return newRequest(http.MethodPatch, target, body, headersQueryParams)143}144func del(target string, body io.Reader, headersQueryParams ...any) (*http.Request, error) {145 return newRequest(http.MethodDelete, target, body, headersQueryParams)146}147// NewRequest creates a new HTTP request as [httptest.NewRequest]148// does, with the ability to immediately add some headers and/or some149// query parameters.150//151// Headers can be added using string pairs as in:152//153// req := tdhttp.NewRequest("POST", "/pdf", body,154// "Content-type", "application/pdf",155// "X-Test", "value",156// )157//158// or using [http.Header] as in:159//160// req := tdhttp.NewRequest("POST", "/pdf", body,161// http.Header{"Content-type": []string{"application/pdf"}},162// )163//164// or using [BasicAuthHeader] as in:165//166// req := tdhttp.NewRequest("POST", "/pdf", body,167// tdhttp.BasicAuthHeader("max", "5ecr3T"),168// )169//170// or using [http.Cookie] (pointer or not, behind the scene,171// [http.Request.AddCookie] is used) as in:172//173// req := tdhttp.NewRequest("POST", "/pdf", body,174// http.Cookie{Name: "cook1", Value: "val1"},175// &http.Cookie{Name: "cook2", Value: "val2"},176// )177//178// Several header sources are combined:179//180// req := tdhttp.NewRequest("POST", "/pdf", body,181// "Content-type", "application/pdf",182// http.Header{"X-Test": []string{"value1"}},183// "X-Test", "value2",184// http.Cookie{Name: "cook1", Value: "val1"},185// tdhttp.BasicAuthHeader("max", "5ecr3T"),186// &http.Cookie{Name: "cook2", Value: "val2"},187// )188//189// Produces the following [http.Header]:190//191// http.Header{192// "Authorization": []string{"Basic bWF4OjVlY3IzVA=="},193// "Content-type": []string{"application/pdf"},194// "Cookie": []string{"cook1=val1; cook2=val2"},195// "X-Test": []string{"value1", "value2"},196// }197//198// A string slice or a map can be flatened as well. As [NewRequest] expects199// ...any, [td.Flatten] can help here too:200//201// strHeaders := map[string]string{202// "X-Length": "666",203// "X-Foo": "bar",204// }205// req := tdhttp.NewRequest("POST", "/pdf", body, td.Flatten(strHeaders))206//207// Or combined with forms seen above:208//209// req := tdhttp.NewRequest("POST", "/pdf", body,210// "Content-type", "application/pdf",211// http.Header{"X-Test": []string{"value1"}},212// td.Flatten(strHeaders),213// "X-Test", "value2",214// http.Cookie{Name: "cook1", Value: "val1"},215// tdhttp.BasicAuthHeader("max", "5ecr3T"),216// &http.Cookie{Name: "cook2", Value: "val2"},217// )218//219// Header keys are always canonicalized using [http.CanonicalHeaderKey].220//221// Query parameters can be added using [url.Values] or more flexible222// [Q], as in:223//224// req := tdhttp.NewRequest("GET", "/pdf",225// url.Values{226// "param": {"val"},227// "names": {"bob", "alice"},228// },229// "X-Test": "a header in the middle",230// tdhttp.Q{231// "limit": 20,232// "ids": []int64{456, 789},233// "details": true,234// },235// )236//237// All [url.Values] and [Q] instances are combined to produce the238// final query string to use. The previous example produces the239// following target:240//241// /pdf?details=true&ids=456&ids=789&limit=20&names=bob&names=alice&param=val242//243// If target already contains a query string, it is reused:244//245// req := tdhttp.NewRequest("GET", "/pdf?limit=10", tdhttp.Q{"details": true})246//247// produces the following target:248//249// /path?details=true&limit=10250//251// Behind the scene, [url.Values.Encode] is used, so the parameters252// are always sorted by key. If you want a specific order, then do not253// use [url.Values] nor [Q] instances, but compose target by yourself.254//255// See [Q] documentation to learn how values are stringified.256func NewRequest(method, target string, body io.Reader, headersQueryParams ...any) *http.Request {257 req, err := newRequest(method, target, body, headersQueryParams)258 if err != nil {259 panic(err)260 }261 return req262}263// Get creates a new HTTP GET. It is a shortcut for:264//265// tdhttp.NewRequest(http.MethodGet, target, nil, headersQueryParams...)266//267// See [NewRequest] for all possible formats accepted in headersQueryParams.268func Get(target string, headersQueryParams ...any) *http.Request {269 req, err := get(target, headersQueryParams...)270 if err != nil {271 panic(err)272 }273 return req274}275// Head creates a new HTTP HEAD. It is a shortcut for:276//277// tdhttp.NewRequest(http.MethodHead, target, nil, headersQueryParams...)278//279// See [NewRequest] for all possible formats accepted in headersQueryParams.280func Head(target string, headersQueryParams ...any) *http.Request {281 req, err := head(target, headersQueryParams...)282 if err != nil {283 panic(err)284 }285 return req286}287// Options creates a HTTP OPTIONS. It is a shortcut for:288//289// tdhttp.NewRequest(http.MethodOptions, target, body, headersQueryParams...)290//291// See [NewRequest] for all possible formats accepted in headersQueryParams.292func Options(target string, body io.Reader, headersQueryParams ...any) *http.Request {293 req, err := options(target, body, headersQueryParams...)294 if err != nil {295 panic(err)296 }297 return req298}299// Post creates a HTTP POST. It is a shortcut for:300//301// tdhttp.NewRequest(http.MethodPost, target, body, headersQueryParams...)302//303// See [NewRequest] for all possible formats accepted in headersQueryParams.304func Post(target string, body io.Reader, headersQueryParams ...any) *http.Request {305 req, err := post(target, body, headersQueryParams...)306 if err != nil {307 panic(err)308 }309 return req310}311// URLValuesEncoder is an interface [PostForm] and [TestAPI.PostForm] data312// must implement.313// Encode can be called to generate a "URL encoded" form such as314// ("bar=baz&foo=quux") sorted by key.315//316// [url.Values] and [Q] implement this interface.317type URLValuesEncoder interface {318 Encode() string319}320// PostForm creates a HTTP POST with data's keys and values321// URL-encoded as the request body. "Content-Type" header is322// automatically set to "application/x-www-form-urlencoded". Other323// headers can be added via headersQueryParams, as in:324//325// req := tdhttp.PostForm("/data",326// url.Values{327// "param1": []string{"val1", "val2"},328// "param2": []string{"zip"},329// },330// "X-Foo", "Foo-value",331// "X-Zip", "Zip-value",332// )333//334// See [NewRequest] for all possible formats accepted in headersQueryParams.335func PostForm(target string, data URLValuesEncoder, headersQueryParams ...any) *http.Request {336 req, err := postForm(target, data, headersQueryParams...)337 if err != nil {338 panic(err)339 }340 return req341}342// PostMultipartFormData creates a HTTP POST multipart request, like343// multipart/form-data one for example. See [MultipartBody] type for344// details. "Content-Type" header is automatically set depending on345// data.MediaType (defaults to "multipart/form-data") and data.Boundary346// (defaults to "go-testdeep-42"). Other headers can be added via347// headersQueryParams, as in:348//349// req := tdhttp.PostMultipartFormData("/data",350// &tdhttp.MultipartBody{351// // "multipart/form-data" by default352// Parts: []*tdhttp.MultipartPart{353// tdhttp.NewMultipartPartString("type", "Sales"),354// tdhttp.NewMultipartPartFile("report", "report.json", "application/json"),355// },356// },357// "X-Foo", "Foo-value",358// "X-Zip", "Zip-value",359// )360//361// and with a different media type:362//363// req := tdhttp.PostMultipartFormData("/data",364// &tdhttp.MultipartBody{365// MediaType: "multipart/mixed",366// Parts: []*tdhttp.MultipartPart{367// tdhttp.NewMultipartPartString("type", "Sales"),368// tdhttp.NewMultipartPartFile("report", "report.json", "application/json"),369// },370// },371// "X-Foo", "Foo-value",372// "X-Zip", "Zip-value",373// )374//375// See [NewRequest] for all possible formats accepted in headersQueryParams.376func PostMultipartFormData(target string, data *MultipartBody, headersQueryParams ...any) *http.Request {377 req, err := postMultipartFormData(target, data, headersQueryParams...)378 if err != nil {379 panic(err)380 }381 return req382}383// Put creates a HTTP PUT. It is a shortcut for:384//385// tdhttp.NewRequest(http.MethodPut, target, body, headersQueryParams...)386//387// See [NewRequest] for all possible formats accepted in headersQueryParams.388func Put(target string, body io.Reader, headersQueryParams ...any) *http.Request {389 req, err := put(target, body, headersQueryParams...)390 if err != nil {391 panic(err)392 }393 return req394}395// Patch creates a HTTP PATCH. It is a shortcut for:396//397// tdhttp.NewRequest(http.MethodPatch, target, body, headersQueryParams...)398//399// See [NewRequest] for all possible formats accepted in headersQueryParams.400func Patch(target string, body io.Reader, headersQueryParams ...any) *http.Request {401 req, err := patch(target, body, headersQueryParams...)402 if err != nil {403 panic(err)404 }405 return req406}407// Delete creates a HTTP DELETE. It is a shortcut for:408//409// tdhttp.NewRequest(http.MethodDelete, target, body, headersQueryParams...)410//411// See [NewRequest] for all possible formats accepted in headersQueryParams.412func Delete(target string, body io.Reader, headersQueryParams ...any) *http.Request {413 req, err := del(target, body, headersQueryParams...)414 if err != nil {415 panic(err)416 }417 return req418}419func newJSONRequest(method, target string, body any, headersQueryParams ...any) (*http.Request, error) {420 b, err := json.Marshal(body)421 if err != nil {422 if opErr, ok := types.AsOperatorNotJSONMarshallableError(err); ok {423 var plus string424 switch op := opErr.Operator(); op {425 case "JSON", "SubJSONOf", "SuperJSONOf":426 plus = ", use json.RawMessage() instead"427 }...

Full Screen

Full Screen

del

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 client := tdhttp.NewClient()4 if err != nil {5 panic(err)6 }7 fmt.Println(resp.Status())8 fmt.Println(resp.Text())9}10import (11func main() {12 client := tdhttp.NewClient()13 if err != nil {14 panic(err)15 }16 fmt.Println(resp.Status())17 fmt.Println(resp.Text())18}19import (20func main() {21 client := tdhttp.NewClient()22 if err != nil {23 panic(err)24 }25 fmt.Println(resp.Status())26 fmt.Println(resp.Text())27}28import (29func main() {30 client := tdhttp.NewClient()31 if err != nil {32 panic(err)33 }34 fmt.Println(resp.Status())35 fmt.Println(resp.Text())36}37import (38func main() {39 client := tdhttp.NewClient()40 if err != nil {41 panic(err)42 }43 fmt.Println(resp.Status())44 fmt.Println(resp.Text())45}46import (47func main() {

Full Screen

Full Screen

del

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 client := tdhttp.New()4 if err != nil {5 fmt.Println("Error Occured")6 }7 fmt.Println(resp.String())8}9{10 "args": {}, 11 "files": {}, 12 "form": {}, 13 "headers": {14 },

Full Screen

Full Screen

del

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 tdhttp := http.NewHTTPHandler()4 tdhttp.Start()5 tdhttp.Del("/del", func(w http.ResponseWriter, r *http.Request, ps map[string]string) {6 fmt.Fprintf(w, "This is a delete method")7 })8}9import (10func main() {11 tdhttp := http.NewHTTPHandler()12 tdhttp.Start()13 tdhttp.Get("/get", func(w http.ResponseWriter, r *http.Request, ps map[string]string) {14 fmt.Fprintf(w, "This is a get method")15 })16}17import (18func main() {19 tdhttp := http.NewHTTPHandler()20 tdhttp.Start()21 tdhttp.Post("/post", func(w http.ResponseWriter, r *http.Request, ps map[string]string) {22 fmt.Fprintf(w, "This is a post method")23 })24}25import (26func main() {27 tdhttp := http.NewHTTPHandler()28 tdhttp.Start()29 tdhttp.Put("/put", func(w http.ResponseWriter, r *http.Request, ps map[string]string) {30 fmt.Fprintf(w, "This is a put method")31 })32}33import (34func main() {

Full Screen

Full Screen

del

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 t := tdhttp.New()4 t.SetMethod("DELETE")5 t.SetHeaders(map[string]string{6 })7 t.SetBody([]byte(`{"name":"John"}`))8 res, err := t.Send()9 if err != nil {10 fmt.Println(err)11 }12 fmt.Println(res)13}14{"message":"deleted"}15import (16func main() {17 t := tdhttp.New()18 t.SetMethod("PATCH")19 t.SetHeaders(map[string]string{20 })21 t.SetBody([]byte(`{"name":"John"}`))22 res, err := t.Send()23 if err != nil {24 fmt.Println(err)25 }26 fmt.Println(res)27}28{"message":"updated"}29import (30func main() {31 t := tdhttp.New()32 t.SetMethod("PUT")33 t.SetHeaders(map[string]string{

Full Screen

Full Screen

del

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 td := tdhttp.New()4 if err != nil {5 fmt.Println(err)6 } else {7 fmt.Println(res)8 }9}10import (11func main() {12 td := tdhttp.New()13 if err != nil {14 fmt.Println(err)15 } else {16 fmt.Println(res)17 }18}19import (20func main() {21 td := tdhttp.New()22 if err != nil {23 fmt.Println(err)24 } else {25 fmt.Println(res)26 }27}28import (29func main() {30 td := tdhttp.New()31 if err != nil {32 fmt.Println(err)33 } else {34 fmt.Println(res)35 }36}37import (38func main() {39 td := tdhttp.New()40 if err != nil {41 fmt.Println(err

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful