Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
75fd1e43b9 | |||
395a3eed3d | |||
3ba8cb7f9e | |||
b07806b9a1 | |||
0f583218d4 |
@@ -124,7 +124,7 @@ func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
return NewOutgoingContext(ctx, md)
|
return NewOutgoingContext(ctx, md)
|
||||||
}
|
}
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd[k] = v
|
omd.Set(k, v)
|
||||||
}
|
}
|
||||||
return NewOutgoingContext(ctx, omd)
|
return NewOutgoingContext(ctx, omd)
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
return NewIncomingContext(ctx, md)
|
return NewIncomingContext(ctx, md)
|
||||||
}
|
}
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd[k] = v
|
omd.Set(k, v)
|
||||||
}
|
}
|
||||||
return NewIncomingContext(ctx, omd)
|
return NewIncomingContext(ctx, omd)
|
||||||
}
|
}
|
||||||
|
@@ -24,7 +24,7 @@ var (
|
|||||||
// Metadata is our way of representing request headers internally.
|
// Metadata is our way of representing request headers internally.
|
||||||
// They're used at the RPC level and translate back and forth
|
// They're used at the RPC level and translate back and forth
|
||||||
// from Transport headers.
|
// from Transport headers.
|
||||||
type Metadata map[string][]string
|
type Metadata map[string]string
|
||||||
|
|
||||||
type rawMetadata struct {
|
type rawMetadata struct {
|
||||||
md Metadata
|
md Metadata
|
||||||
@@ -42,7 +42,7 @@ type Iterator struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Next advance iterator to next element
|
// Next advance iterator to next element
|
||||||
func (iter *Iterator) Next(k *string, v *[]string) bool {
|
func (iter *Iterator) Next(k, v *string) bool {
|
||||||
if iter.cur+1 > iter.cnt {
|
if iter.cur+1 > iter.cnt {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -64,11 +64,8 @@ func (md Metadata) Iterator() *Iterator {
|
|||||||
return iter
|
return iter
|
||||||
}
|
}
|
||||||
|
|
||||||
// Values returns values from metadata by key
|
// Get returns value from metadata by key
|
||||||
func (md Metadata) Values(key string) ([]string, bool) {
|
func (md Metadata) Get(key string) (string, bool) {
|
||||||
if md == nil {
|
|
||||||
return nil, false
|
|
||||||
}
|
|
||||||
// fast path
|
// fast path
|
||||||
val, ok := md[key]
|
val, ok := md[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -78,42 +75,16 @@ func (md Metadata) Values(key string) ([]string, bool) {
|
|||||||
return val, ok
|
return val, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns value from metadata by key
|
|
||||||
func (md Metadata) Get(key string) (string, bool) {
|
|
||||||
if md == nil {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
// fast path
|
|
||||||
val, ok := md[key]
|
|
||||||
if !ok {
|
|
||||||
// slow path
|
|
||||||
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
|
|
||||||
}
|
|
||||||
if len(val) == 0 {
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
return val[0], ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set is used to store value in metadata
|
// Set is used to store value in metadata
|
||||||
func (md Metadata) Set(kv ...string) {
|
func (md Metadata) Set(kv ...string) {
|
||||||
if md == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if len(kv)%2 == 1 {
|
if len(kv)%2 == 1 {
|
||||||
kv = kv[:len(kv)-1]
|
kv = kv[:len(kv)-1]
|
||||||
}
|
}
|
||||||
for idx := 0; idx < len(kv); idx += 2 {
|
for idx := 0; idx < len(kv); idx += 2 {
|
||||||
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = []string{kv[idx+1]}
|
md[textproto.CanonicalMIMEHeaderKey(kv[idx])] = kv[idx+1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add is used to append value in metadata
|
|
||||||
func (md Metadata) Add(k string, v string) {
|
|
||||||
kn := textproto.CanonicalMIMEHeaderKey(k)
|
|
||||||
md[kn] = append(md[kn], v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Del is used to remove value from metadata
|
// Del is used to remove value from metadata
|
||||||
func (md Metadata) Del(keys ...string) {
|
func (md Metadata) Del(keys ...string) {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
@@ -128,7 +99,7 @@ func (md Metadata) Del(keys ...string) {
|
|||||||
func Copy(md Metadata) Metadata {
|
func Copy(md Metadata) Metadata {
|
||||||
nmd := New(len(md))
|
nmd := New(len(md))
|
||||||
for key, val := range md {
|
for key, val := range md {
|
||||||
nmd[key] = val
|
nmd.Set(key, val)
|
||||||
}
|
}
|
||||||
return nmd
|
return nmd
|
||||||
}
|
}
|
||||||
@@ -143,24 +114,16 @@ func New(size int) Metadata {
|
|||||||
|
|
||||||
// Merge merges metadata to existing metadata, overwriting if specified
|
// Merge merges metadata to existing metadata, overwriting if specified
|
||||||
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
||||||
|
var ok bool
|
||||||
nmd := Copy(omd)
|
nmd := Copy(omd)
|
||||||
for key, nval := range mmd {
|
for key, val := range mmd {
|
||||||
oval, ok := nmd[key]
|
_, ok = nmd[key]
|
||||||
switch {
|
switch {
|
||||||
case ok && !overwrite:
|
case ok && !overwrite:
|
||||||
continue
|
continue
|
||||||
case len(nval) == 1 && nval[0] != "":
|
case val != "":
|
||||||
nmd[key] = nval
|
nmd.Set(key, val)
|
||||||
case ok && len(nval) > 1:
|
case ok && val == "":
|
||||||
sort.Strings(nval)
|
|
||||||
sort.Strings(oval)
|
|
||||||
for idx, v := range nval {
|
|
||||||
if oval[idx] != v {
|
|
||||||
oval[idx] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nmd[key] = oval
|
|
||||||
case ok && nval[0] == "":
|
|
||||||
nmd.Del(key)
|
nmd.Del(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -68,10 +68,10 @@ func TestPassing(t *testing.T) {
|
|||||||
|
|
||||||
func TestMerge(t *testing.T) {
|
func TestMerge(t *testing.T) {
|
||||||
omd := Metadata{
|
omd := Metadata{
|
||||||
"key1": []string{"val1"},
|
"key1": "val1",
|
||||||
}
|
}
|
||||||
mmd := Metadata{
|
mmd := Metadata{
|
||||||
"key2": []string{"val2"},
|
"key2": "val2",
|
||||||
}
|
}
|
||||||
|
|
||||||
nmd := Merge(omd, mmd, true)
|
nmd := Merge(omd, mmd, true)
|
||||||
@@ -82,14 +82,13 @@ func TestMerge(t *testing.T) {
|
|||||||
|
|
||||||
func TestIterator(t *testing.T) {
|
func TestIterator(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"1Last": []string{"last"},
|
"1Last": "last",
|
||||||
"2First": []string{"first"},
|
"2First": "first",
|
||||||
"3Second": []string{"second"},
|
"3Second": "second",
|
||||||
}
|
}
|
||||||
|
|
||||||
iter := md.Iterator()
|
iter := md.Iterator()
|
||||||
var k string
|
var k, v string
|
||||||
var v []string
|
|
||||||
|
|
||||||
for iter.Next(&k, &v) {
|
for iter.Next(&k, &v) {
|
||||||
// fmt.Printf("k: %s, v: %s\n", k, v)
|
// fmt.Printf("k: %s, v: %s\n", k, v)
|
||||||
@@ -102,20 +101,20 @@ func TestMedataCanonicalKey(t *testing.T) {
|
|||||||
v, ok := md.Get("x-request-id")
|
v, ok := md.Get("x-request-id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if len(v) != 1 && v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok = md.Get("X-Request-Id")
|
v, ok = md.Get("X-Request-Id")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if len(v) != 1 && v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
v, ok = md.Get("X-Request-ID")
|
v, ok = md.Get("X-Request-ID")
|
||||||
if !ok {
|
if !ok {
|
||||||
t.Fatalf("failed to get x-request-id")
|
t.Fatalf("failed to get x-request-id")
|
||||||
} else if len(v) != 1 && v != "12345" {
|
} else if v != "12345" {
|
||||||
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,8 +135,8 @@ func TestMetadataSet(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataDelete(t *testing.T) {
|
func TestMetadataDelete(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": []string{"bar"},
|
"Foo": "bar",
|
||||||
"Baz": []string{"empty"},
|
"Baz": "empty",
|
||||||
}
|
}
|
||||||
|
|
||||||
md.Del("Baz")
|
md.Del("Baz")
|
||||||
@@ -158,14 +157,14 @@ func TestNilContext(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataCopy(t *testing.T) {
|
func TestMetadataCopy(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": []string{"bar"},
|
"Foo": "bar",
|
||||||
"Bar": []string{"baz"},
|
"Bar": "baz",
|
||||||
}
|
}
|
||||||
|
|
||||||
cp := Copy(md)
|
cp := Copy(md)
|
||||||
|
|
||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
if cv := cp[k]; len(cv) != len(v) || (cv[0] != v[0] && cv[1] != v[1]) {
|
if cv := cp[k]; cv != v {
|
||||||
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
t.Fatalf("Got %s:%s for %s:%s", k, cv, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +172,7 @@ func TestMetadataCopy(t *testing.T) {
|
|||||||
|
|
||||||
func TestMetadataContext(t *testing.T) {
|
func TestMetadataContext(t *testing.T) {
|
||||||
md := Metadata{
|
md := Metadata{
|
||||||
"Foo": []string{"bar"},
|
"Foo": "bar",
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := NewContext(context.TODO(), md)
|
ctx := NewContext(context.TODO(), md)
|
||||||
@@ -183,7 +182,7 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
t.Errorf("Unexpected error retrieving metadata, got %t", ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(emd["Foo"]) != len(md["Foo"]) || (emd["Foo"][0] != md["Foo"][0]) {
|
if emd["Foo"] != md["Foo"] {
|
||||||
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
t.Errorf("Expected key: %s val: %s, got key: %s val: %s", "Foo", md["Foo"], "Foo", emd["Foo"])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
md "go.unistack.org/micro/v3/metadata"
|
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ type node struct {
|
|||||||
type record struct {
|
type record struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Metadata md.Metadata
|
Metadata map[string]string
|
||||||
Nodes map[string]*node
|
Nodes map[string]*node
|
||||||
Endpoints []*Endpoint
|
Endpoints []*Endpoint
|
||||||
}
|
}
|
||||||
@@ -137,9 +136,9 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
|
|
||||||
// domain is set in metadata so it can be passed to watchers
|
// domain is set in metadata so it can be passed to watchers
|
||||||
if s.Metadata == nil {
|
if s.Metadata == nil {
|
||||||
s.Metadata = map[string][]string{"domain": {options.Domain}}
|
s.Metadata = map[string]string{"domain": options.Domain}
|
||||||
} else {
|
} else {
|
||||||
s.Metadata["domain"] = []string{options.Domain}
|
s.Metadata["domain"] = options.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure the service name exists
|
// ensure the service name exists
|
||||||
@@ -165,10 +164,15 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata := md.Copy(n.Metadata)
|
metadata := make(map[string]string, len(n.Metadata))
|
||||||
|
|
||||||
|
// make copy of metadata
|
||||||
|
for k, v := range n.Metadata {
|
||||||
|
metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
// set the domain
|
// set the domain
|
||||||
metadata["domain"] = []string{options.Domain}
|
metadata["domain"] = options.Domain
|
||||||
|
|
||||||
// add the node
|
// add the node
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
srvs[s.Name][s.Version].Nodes[n.ID] = &node{
|
||||||
@@ -212,9 +216,9 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO
|
|||||||
|
|
||||||
// domain is set in metadata so it can be passed to watchers
|
// domain is set in metadata so it can be passed to watchers
|
||||||
if s.Metadata == nil {
|
if s.Metadata == nil {
|
||||||
s.Metadata = map[string][]string{"domain": {options.Domain}}
|
s.Metadata = map[string]string{"domain": options.Domain}
|
||||||
} else {
|
} else {
|
||||||
s.Metadata["domain"] = []string{options.Domain}
|
s.Metadata["domain"] = options.Domain
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the domain doesn't exist, there is nothing to deregister
|
// if the domain doesn't exist, there is nothing to deregister
|
||||||
@@ -422,8 +426,8 @@ func (m *watcher) Next() (*Result, error) {
|
|||||||
|
|
||||||
// extract domain from service metadata
|
// extract domain from service metadata
|
||||||
var domain string
|
var domain string
|
||||||
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 && len(r.Service.Metadata["domain"][0]) > 0 {
|
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 {
|
||||||
domain = r.Service.Metadata["domain"][0]
|
domain = r.Service.Metadata["domain"]
|
||||||
} else {
|
} else {
|
||||||
domain = DefaultDomain
|
domain = DefaultDomain
|
||||||
}
|
}
|
||||||
@@ -448,7 +452,10 @@ func (m *watcher) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func serviceToRecord(s *Service, ttl time.Duration) *record {
|
func serviceToRecord(s *Service, ttl time.Duration) *record {
|
||||||
metadata := md.Copy(s.Metadata)
|
metadata := make(map[string]string, len(s.Metadata))
|
||||||
|
for k, v := range s.Metadata {
|
||||||
|
metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
nodes := make(map[string]*node, len(s.Nodes))
|
nodes := make(map[string]*node, len(s.Nodes))
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
@@ -474,28 +481,41 @@ func serviceToRecord(s *Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func recordToService(r *record, domain string) *Service {
|
func recordToService(r *record, domain string) *Service {
|
||||||
metadata := md.Copy(r.Metadata)
|
metadata := make(map[string]string, len(r.Metadata))
|
||||||
|
for k, v := range r.Metadata {
|
||||||
|
metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
// set the domain in metadata so it can be determined when a wildcard query is performed
|
// set the domain in metadata so it can be determined when a wildcard query is performed
|
||||||
metadata["domain"] = []string{domain}
|
metadata["domain"] = domain
|
||||||
|
|
||||||
endpoints := make([]*Endpoint, len(r.Endpoints))
|
endpoints := make([]*Endpoint, len(r.Endpoints))
|
||||||
for i, e := range r.Endpoints {
|
for i, e := range r.Endpoints {
|
||||||
|
md := make(map[string]string, len(e.Metadata))
|
||||||
|
for k, v := range e.Metadata {
|
||||||
|
md[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
endpoints[i] = &Endpoint{
|
endpoints[i] = &Endpoint{
|
||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Request: e.Request,
|
Request: e.Request,
|
||||||
Response: e.Response,
|
Response: e.Response,
|
||||||
Metadata: md.Copy(e.Metadata),
|
Metadata: md,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]*Node, len(r.Nodes))
|
nodes := make([]*Node, len(r.Nodes))
|
||||||
i := 0
|
i := 0
|
||||||
for _, n := range r.Nodes {
|
for _, n := range r.Nodes {
|
||||||
|
md := make(map[string]string, len(n.Metadata))
|
||||||
|
for k, v := range n.Metadata {
|
||||||
|
md[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
nodes[i] = &Node{
|
nodes[i] = &Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: md.Copy(n.Metadata),
|
Metadata: md,
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
@@ -42,3 +42,13 @@ func SetSubscriberOption(k, v interface{}) SubscriberOption {
|
|||||||
o.Context = context.WithValue(o.Context, k, v)
|
o.Context = context.WithValue(o.Context, k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetHandlerOption returns a function to setup a context with given value
|
||||||
|
func SetHandlerOption(k, v interface{}) HandlerOption {
|
||||||
|
return func(o *HandlerOptions) {
|
||||||
|
if o.Context == nil {
|
||||||
|
o.Context = context.Background()
|
||||||
|
}
|
||||||
|
o.Context = context.WithValue(o.Context, k, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -322,7 +322,7 @@ type HandlerOption func(*HandlerOptions)
|
|||||||
type HandlerOptions struct {
|
type HandlerOptions struct {
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Metadata for hondler
|
// Metadata for handler
|
||||||
Metadata map[string]metadata.Metadata
|
Metadata map[string]metadata.Metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package time
|
package time
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -46,3 +47,36 @@ func ParseDuration(s string) (time.Duration, error) {
|
|||||||
|
|
||||||
return td, err
|
return td, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d Duration) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(time.Duration(d).String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Duration) UnmarshalJSON(b []byte) error {
|
||||||
|
var v interface{}
|
||||||
|
if err := json.Unmarshal(b, &v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch value := v.(type) {
|
||||||
|
case float64:
|
||||||
|
*d = Duration(time.Duration(value))
|
||||||
|
return nil
|
||||||
|
case string:
|
||||||
|
dv, err := time.ParseDuration(value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*d = Duration(dv)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid duration")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func (d Duration) MarshalYAML() (interface{}, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Duration) UnmarshalYAML(fn func(interface{}) error) error
|
||||||
|
*/
|
||||||
|
@@ -1,10 +1,37 @@
|
|||||||
package time
|
package time
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMarshalJSON(t *testing.T) {
|
||||||
|
d := Duration(10000000)
|
||||||
|
buf, err := json.Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(buf, []byte(`"10ms"`)) {
|
||||||
|
t.Fatalf("invalid duration: %s != %s", buf, `"10ms"`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalJSON(t *testing.T) {
|
||||||
|
type str struct {
|
||||||
|
TTL Duration `json:"ttl"`
|
||||||
|
}
|
||||||
|
v := &str{}
|
||||||
|
|
||||||
|
err := json.Unmarshal([]byte(`{"ttl":"10ms"}`), v)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v.TTL != 10000000 {
|
||||||
|
t.Fatalf("invalid duration %v != 10000000", v.TTL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParseDuration(t *testing.T) {
|
func TestParseDuration(t *testing.T) {
|
||||||
var td time.Duration
|
var td time.Duration
|
||||||
var err error
|
var err error
|
||||||
|
Reference in New Issue
Block a user