github.com/google/go-github/v53@v53.2.0/test/fields/fields.go (about)

     1  // Copyright 2014 The go-github AUTHORS. All rights reserved.
     2  //
     3  // Use of this source code is governed by a BSD-style
     4  // license that can be found in the LICENSE file.
     5  
     6  // This tool tests for the JSON mappings in the go-github data types. It will
     7  // identify fields that are returned by the live GitHub API, but that are not
     8  // currently mapped into a struct field of the relevant go-github type. This
     9  // helps to ensure that all relevant data returned by the API is being made
    10  // accessible, particularly new fields that are periodically (and sometimes
    11  // quietly) added to the API over time.
    12  //
    13  // These tests simply aid in identifying which fields aren't being mapped; it
    14  // is not necessarily true that every one of them should always be mapped.
    15  // Some fields may be undocumented for a reason, either because they aren't
    16  // actually used yet or should not be relied upon.
    17  package main
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"flag"
    23  	"fmt"
    24  	"os"
    25  	"reflect"
    26  	"strings"
    27  
    28  	"github.com/google/go-github/v53/github"
    29  )
    30  
    31  var (
    32  	client *github.Client
    33  
    34  	// auth indicates whether tests are being run with an OAuth token.
    35  	// Tests can use this flag to skip certain tests when run without auth.
    36  	auth bool
    37  
    38  	skipURLs = flag.Bool("skip_urls", false, "skip url fields")
    39  )
    40  
    41  func main() {
    42  	flag.Parse()
    43  
    44  	token := os.Getenv("GITHUB_AUTH_TOKEN")
    45  	if token == "" {
    46  		print("!!! No OAuth token. Some tests won't run. !!!\n\n")
    47  		client = github.NewClient(nil)
    48  	} else {
    49  		client = github.NewTokenClient(context.Background(), token)
    50  		auth = true
    51  	}
    52  
    53  	for _, tt := range []struct {
    54  		url string
    55  		typ interface{}
    56  	}{
    57  		//{"rate_limit", &github.RateLimits{}},
    58  		{"users/octocat", &github.User{}},
    59  		{"user", &github.User{}},
    60  		{"users/willnorris/keys", &[]github.Key{}},
    61  		{"orgs/google-test", &github.Organization{}},
    62  		{"repos/google/go-github", &github.Repository{}},
    63  		{"repos/google/go-github/issues/1", &github.Issue{}},
    64  		{"/gists/9257657", &github.Gist{}},
    65  	} {
    66  		err := testType(tt.url, tt.typ)
    67  		if err != nil {
    68  			fmt.Printf("error: %v\n", err)
    69  		}
    70  	}
    71  }
    72  
    73  // testType fetches the JSON resource at urlStr and compares its keys to the
    74  // struct fields of typ.
    75  func testType(urlStr string, typ interface{}) error {
    76  	slice := reflect.Indirect(reflect.ValueOf(typ)).Kind() == reflect.Slice
    77  
    78  	req, err := client.NewRequest("GET", urlStr, nil)
    79  	if err != nil {
    80  		return err
    81  	}
    82  
    83  	// start with a json.RawMessage so we can decode multiple ways below
    84  	raw := new(json.RawMessage)
    85  	_, err = client.Do(context.Background(), req, raw)
    86  	if err != nil {
    87  		return err
    88  	}
    89  
    90  	// unmarshal directly to a map
    91  	var m1 map[string]interface{}
    92  	if slice {
    93  		var s []map[string]interface{}
    94  		err = json.Unmarshal(*raw, &s)
    95  		if err != nil {
    96  			return err
    97  		}
    98  		m1 = s[0]
    99  	} else {
   100  		err = json.Unmarshal(*raw, &m1)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	// unmarshal to typ first, then re-marshal and unmarshal to a map
   107  	err = json.Unmarshal(*raw, typ)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	var byt []byte
   113  	if slice {
   114  		// use first item in slice
   115  		v := reflect.Indirect(reflect.ValueOf(typ))
   116  		byt, err = json.Marshal(v.Index(0).Interface())
   117  		if err != nil {
   118  			return err
   119  		}
   120  	} else {
   121  		byt, err = json.Marshal(typ)
   122  		if err != nil {
   123  			return err
   124  		}
   125  	}
   126  
   127  	var m2 map[string]interface{}
   128  	err = json.Unmarshal(byt, &m2)
   129  	if err != nil {
   130  		return err
   131  	}
   132  
   133  	// now compare the two maps
   134  	for k, v := range m1 {
   135  		if *skipURLs && strings.HasSuffix(k, "_url") {
   136  			continue
   137  		}
   138  		if _, ok := m2[k]; !ok {
   139  			fmt.Printf("%v missing field for key: %v (example value: %v)\n", reflect.TypeOf(typ), k, v)
   140  		}
   141  	}
   142  
   143  	return nil
   144  }