text:
package controllers

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"reflect"
	"sort"
	"strings"
	"testing"
	"time"

	"github.com/go-logr/logr"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/client-go/tools/record"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/reconcile"

	cronanything "github.com/GoogleCloudPlatform/elcarro-oracle-operator/common/api/v1alpha1"
)

const (
	apiVersion      = "resource.k8s.io/v1alpha1"
	kind            = "resource"
	name            = "name"
	namespace       = "namespace"
	defaultCronExpr = "* * * * *"
)

var (
	baseTime = time.Date(2018, time.April, 20, 4, 20, 30, 0, time.UTC)
)

type fakeCronAnything struct {
	runtime.Object
	metav1.TypeMeta
	metav1.ObjectMeta

	Spec   cronanything.CronAnythingSpec   `json:"spec,omitempty"`
	Status cronanything.CronAnythingStatus `json:"status,omitempty"`
}

func (in *fakeCronAnything) CronAnythingSpec() *cronanything.CronAnythingSpec {
	return &in.Spec
}

func (in *fakeCronAnything) CronAnythingStatus() *cronanything.CronAnythingStatus {
	return &in.Status
}

func (in *fakeCronAnything) GetObjectKind() schema.ObjectKind {
	return schema.EmptyObjectKind
}

func createReconciler() (*ReconcileCronAnything, *fakeCronAnythingControl, *fakeResourceControl) {
	fakeRecorder := &record.FakeRecorder{}
	fakeCronAnythingControl := &fakeCronAnythingControl{}
	fakeResourceControl := &fakeResourceControl{
		deleteSlice: make([]string, 0),
	}
	fakeResourceResolver := &fakeResourceResolver{}
	return &ReconcileCronAnything{
		Log:                 ctrl.Log.WithName("controllers").WithName("CronAnything"),
		cronanythingControl: fakeCronAnythingControl,
		scheme:              runtime.NewScheme(),
		resourceResolver:    fakeResourceResolver,
		resourceControl:     fakeResourceControl,
		eventRecorder:       fakeRecorder,
		nextTrigger:         make(map[string]time.Time),
		currentTime:         func() time.Time { return baseTime },
	}, fakeCronAnythingControl, fakeResourceControl
}

func newFakeCronAnything(apiVersion, kind, name, namespace string) *fakeCronAnything {
	return &fakeCronAnything{
		ObjectMeta: metav1.ObjectMeta{
			CreationTimestamp: metav1.Time{
				Time: baseTime.Add(-60 * time.Second),
			},
			Name:      name,
			Namespace: namespace,
			UID:       "myUID",
		},
		Spec: cronanything.CronAnythingSpec{
			Schedule: defaultCronExpr,
			Template: runtime.RawExtension{
				Raw: []byte(fmt.Sprintf("{\"apiVersion\": \"%s\", \"kind\": \"%s\"}", apiVersion, kind)),
			},
		},
	}
}

func newUnstructuredResource(ca cronanything.CronAnything, name string) *unstructured.Unstructured {
	return &unstructured.Unstructured{
		Object: map[string]interface{}{
			"apiVersion": "v1",
			"kind":       "Resource",
			"metadata": map[string]interface{}{
				"namespace": "default",
				"name":      name,
				"uid":       name,
				"ownerReferences": []interface{}{
					map[string]interface{}{
						"apiVersion": controllerKind.GroupVersion().String(),
						"kind":       controllerKind.Kind,
						"uid":        string(ca.GetUID()),
						"controller": true,
					},
				},
			},
			"status": map[string]interface{}{
				"finishedAt": "",
			},
		},
	}
}

func TestCreateNewResourceOnTrigger(t *testing.T) {
	tVal := true
	fVal := false
	testCases := map[string]struct {
		cascadeDelete *bool
		hasOwnerRef   bool
	}{
		"cascadeDelete is not set, so no owner ref": {
			cascadeDelete: nil,
			hasOwnerRef:   false,
		},
		"cascadeDelete is false, so no owner ref": {
			cascadeDelete: &fVal,
			hasOwnerRef:   false,
		},
		"cascadeDelete is true, so has owner ref": {
			cascadeDelete: &tVal,
			hasOwnerRef:   true,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

			ca := newFakeCronAnything(apiVersion, kind, name, namespace)
			ca.Spec.CascadeDelete = tc.cascadeDelete

			fakeCronAnythingControl.getCronAnything = ca

			fakeResourceControl.listResult = []*unstructured.Unstructured{
				newUnstructuredResource(ca, "resource1"),
				newUnstructuredResource(ca, "resource2"),
			}

			result, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if err != nil {
				t.Error(err)
			}

			if result.RequeueAfter == time.Duration(0) {
				t.Error("Expected result to have RequeueAfter, but it didn't")
			}

			expectedDuration, _ := time.ParseDuration("30s")
			if result.RequeueAfter != expectedDuration {
				t.Errorf("Expected next requeue to be after %f seconds, but it was %f seconds", expectedDuration.Seconds(), result.RequeueAfter.Seconds())
			}

			createTemplate := fakeResourceControl.createTemplate
			if createTemplate.Object["apiVersion"] != apiVersion {
				t.Errorf("Expected apiVersion to be %s, but found %s", apiVersion, createTemplate.Object["apiVersion"])
			}
			if createTemplate.Object["kind"] != kind {
				t.Errorf("Expected kind to be %s, but found %s", kind, createTemplate.Object["kind"])
			}

			if tc.hasOwnerRef && len(createTemplate.GetOwnerReferences()) == 0 {
				t.Errorf("Expected resource to have owner reference, but it didn't")
			}
			if !tc.hasOwnerRef && len(createTemplate.GetOwnerReferences()) == 1 {
				t.Errorf("Expected resource to have no owner reference, but it did")
			}
		})
	}
}

func TestAlreadyDeletedCronAnythingResource(t *testing.T) {
	reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

	ca := newFakeCronAnything(apiVersion, kind, name, namespace)
	ca.DeletionTimestamp = &metav1.Time{
		Time: baseTime,
	}
	ca.Status.LastScheduleTime = &metav1.Time{
		Time: baseTime.Add(-10 * time.Hour),
	}
	fakeCronAnythingControl.getCronAnything = ca

	result, err := reconciler.Reconcile(context.Background(), reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: namespace,
			Name:      name,
		},
	})
	if err != nil {
		t.Error(err)
	}

	if result.RequeueAfter != time.Duration(0) || result.Requeue != false {
		t.Errorf("Expected request to not be reqeueued, but found %v", result)
	}

	if fakeResourceControl.createTemplate != nil {
		t.Errorf("Expected no new resource to have been created")
	}
}

func TestSuspended(t *testing.T) {
	reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

	ca := newFakeCronAnything(apiVersion, kind, name, namespace)
	T := true
	ca.Spec.Suspend = &T
	ca.Status.LastScheduleTime = &metav1.Time{
		Time: baseTime.Add(-10 * time.Hour),
	}
	fakeCronAnythingControl.getCronAnything = ca

	result, err := reconciler.Reconcile(context.Background(), reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: namespace,
			Name:      name,
		},
	})
	if err != nil {
		t.Error(err)
	}

	if result.RequeueAfter != time.Duration(0) || result.Requeue != false {
		t.Errorf("Expected request to not be reqeueued, but found %v", result)
	}

	if fakeResourceControl.createTemplate != nil {
		t.Errorf("Expected no new resource to have been created")
	}
}

func TestScheduleTrigger(t *testing.T) {
	testCases := map[string]struct {
		currentTime      time.Time
		lastScheduleTime time.Time
		schedule         string

		expectCreate bool
	}{
		"single past deadline": {
			baseTime,
			baseTime.Add(-1 * time.Minute),
			defaultCronExpr,
			true,
		},
		"multiple past deadline": {
			baseTime,
			baseTime.Add(-1 * time.Hour),
			defaultCronExpr,
			true,
		},
		"none past deadline": {
			baseTime,
			baseTime.Add(-1 * time.Hour),
			"0 0 ? 12 ?",
			false,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

			ca := newFakeCronAnything(apiVersion, kind, namespace, name)
			ca.Status.LastScheduleTime = &metav1.Time{
				Time: tc.lastScheduleTime,
			}
			ca.Spec.Schedule = tc.schedule

			fakeCronAnythingControl.getCronAnything = ca

			_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if err != nil {
				t.Error(err)
			}

			didCreate := fakeResourceControl.createTemplate != nil
			if didCreate != tc.expectCreate {
				t.Errorf("Expected create: %t, did create: %t", tc.expectCreate, didCreate)
			}
		})
	}
}

func TestTriggerDeadline(t *testing.T) {
	reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

	ca := newFakeCronAnything(apiVersion, kind, namespace, name)
	ca.Status.LastScheduleTime = &metav1.Time{
		Time: baseTime.Add(-60 * time.Second),
	}
	twenty := int64(20)
	ca.Spec.TriggerDeadlineSeconds = &twenty
	fakeCronAnythingControl.getCronAnything = ca

	result, err := reconciler.Reconcile(context.Background(), reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: namespace,
			Name:      name,
		},
	})
	if err != nil {
		t.Error(err)
	}

	if result.RequeueAfter == time.Duration(0) {
		t.Errorf("Expected request to be reqeueued, but found %v", result)
	}

	if fakeResourceControl.createCount != 0 {
		t.Errorf("Expected no resource to be created, but found one")
	}
}

func TestIsFinished(t *testing.T) {
	timestampFieldPath := "{.status.myCustomField}"
	stringFieldPath := "{.status.myCustomPhase}"
	testCases := map[string]struct {
		ca       *fakeCronAnything
		resource *unstructured.Unstructured
		result   bool
	}{
		"no strategy defined means not finished": {
			&fakeCronAnything{},
			&unstructured.Unstructured{},
			false,
		},
		"timestamp strategy, custom correct timestamp": {
			&fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyTimestampField,
						TimestampField: &cronanything.TimestampFieldStrategy{
							FieldPath: timestampFieldPath,
						},
					},
				},
			},
			&unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"myCustomField": baseTime.Format(time.RFC3339),
					},
				},
			},
			true,
		},
		"timestamp strategy, custom incorrect timestamp": {
			&fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyTimestampField,
						TimestampField: &cronanything.TimestampFieldStrategy{
							FieldPath: timestampFieldPath,
						},
					},
				},
			},
			&unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"myCustomField": "incorrectTimestamp",
					},
				},
			},
			false,
		},
		"timeestamp strategy, custom field does not exist": {
			&fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyTimestampField,
						TimestampField: &cronanything.TimestampFieldStrategy{
							FieldPath: timestampFieldPath,
						},
					},
				},
			},
			&unstructured.Unstructured{
				Object: map[string]interface{}{},
			},
			false,
		},
		"timestamp strategy, custom advanced jsonpath expression": {
			ca: &fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyTimestampField,
						TimestampField: &cronanything.TimestampFieldStrategy{
							FieldPath: `{.status.conditions[?(@.reason=="PodCompleted")].lastTransitionTime}`,
						},
					},
				},
			},
			resource: &unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"conditions": []interface{}{
							map[string]interface{}{
								"lastTransitionTime": "2018-08-23T22:42:51Z",
								"status":             "True",
								"type":               "Initialized",
							},
							map[string]interface{}{
								"lastTransitionTime": "2018-08-23T22:43:06Z",
								"status":             "True",
								"type":               "Ready",
							},
							map[string]interface{}{
								"lastTransitionTime": "2018-08-23T22:42:51Z",
								"status":             "True",
								"type":               "PodScheduled",
							},
							map[string]interface{}{
								"lastTransitionTime": "2018-09-19T19:26:00Z",
								"reason":             "PodCompleted",
								"status":             "True",
								"type":               "Initialized",
							},
						},
					},
				},
			},
			result: true,
		},
		"string strategy, not finished": {
			&fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyStringField,
						StringField: &cronanything.StringFieldStrategy{
							FieldPath:      stringFieldPath,
							FinishedValues: []string{"Finished", "Failed"},
						},
					},
				},
			},
			&unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"myCustomPhase": "Running",
					},
				},
			},
			false,
		},
		"string strategy, finished": {
			&fakeCronAnything{
				Spec: cronanything.CronAnythingSpec{
					FinishableStrategy: &cronanything.FinishableStrategy{
						Type: cronanything.FinishableStrategyStringField,
						StringField: &cronanything.StringFieldStrategy{
							FieldPath:      stringFieldPath,
							FinishedValues: []string{"Finished", "Failed"},
						},
					},
				},
			},
			&unstructured.Unstructured{
				Object: map[string]interface{}{
					"status": map[string]interface{}{
						"myCustomPhase": "Finished",
					},
				},
			},
			true,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			res, _ := isFinished(tc.ca, tc.resource)
			if res != tc.result {
				t.Errorf("Expected %t, but got %t", tc.result, res)
			}
		})
	}
}

func TestConcurrencyPolicy(t *testing.T) {
	testCases := map[string]struct {
		concurrencyPolicy             cronanything.ConcurrencyPolicy
		existingActiveResourceCount   int
		existingFinishedResourceCount int
		expectedDeleteCount           int
		expectedCreateCount           int
	}{
		"allow concurrent, existing resource": {
			cronanything.AllowConcurrent,
			1,
			1,
			0,
			1,
		},
		"allow concurrent, no existing resources": {
			cronanything.AllowConcurrent,
			0,
			0,
			0,
			1,
		},
		"forbid concurrent, existing active resource": {
			cronanything.ForbidConcurrent,
			1,
			0,
			0,
			0,
		},
		"forbid concurrent, existing finished resource": {
			cronanything.ForbidConcurrent,
			0,
			1,
			0,
			1,
		},
		"forbid concurrent, no existing resources": {
			cronanything.ForbidConcurrent,
			0,
			0,
			0,
			1,
		},
		"replace concurrent, existing active resource": {
			cronanything.ReplaceConcurrent,
			1,
			0,
			1,
			0,
		},
		"replace concurrent, existing finished resource": {
			cronanything.ReplaceConcurrent,
			0,
			2,
			0,
			1,
		},
		"replace concurrent, no existing resources": {
			cronanything.ReplaceConcurrent,
			0,
			0,
			0,
			1,
		},
		"replace concurrent, multiple active resources": {
			cronanything.ReplaceConcurrent,
			5,
			2,
			5,
			0,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

			ca := newFakeCronAnything(apiVersion, kind, namespace, name)
			ca.Spec.ConcurrencyPolicy = tc.concurrencyPolicy
			ca.Spec.FinishableStrategy = &cronanything.FinishableStrategy{
				Type: cronanything.FinishableStrategyTimestampField,
				TimestampField: &cronanything.TimestampFieldStrategy{
					FieldPath: `{.status.finishedAt}`,
				},
			}

			fakeCronAnythingControl.getCronAnything = ca

			var resourceListResult []*unstructured.Unstructured
			for i := 0; i < tc.existingActiveResourceCount; i++ {
				resourceListResult = append(resourceListResult, newUnstructuredResource(ca, fmt.Sprintf("activeResource%d", i)))
			}
			for i := 0; i < tc.existingFinishedResourceCount; i++ {
				resource := newUnstructuredResource(ca, fmt.Sprintf("finishedResource%d", i))
				unstructured.SetNestedField(resource.Object, baseTime.Format(time.RFC3339), "status", "finishedAt")
				resourceListResult = append(resourceListResult, resource)
			}
			fakeResourceControl.listResult = resourceListResult

			_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if err != nil {
				t.Error(err)
			}

			if len(fakeResourceControl.deleteSlice) != tc.expectedDeleteCount {
				t.Errorf("Expected %d deletes, but found %d", tc.expectedDeleteCount, len(fakeResourceControl.deleteSlice))
			}

			if fakeResourceControl.createCount != tc.expectedCreateCount {
				t.Error("Expected resource to be created, but it wasn't")
			}
		})
	}
}

func TestReplaceConcurrentIgnoreResourcesMarkedForDeletion(t *testing.T) {
	reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

	ca := newFakeCronAnything(apiVersion, kind, namespace, name)
	ca.Spec.ConcurrencyPolicy = cronanything.ReplaceConcurrent
	fakeCronAnythingControl.getCronAnything = ca

	resource := newUnstructuredResource(ca, "resource")
	unstructured.SetNestedField(resource.Object, baseTime.Format(time.RFC3339), "metadata", "deletionTimestamp")
	fakeResourceControl.listResult = []*unstructured.Unstructured{resource}

	_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: namespace,
			Name:      name,
		},
	})
	if err != nil {
		t.Error(err)
	}

	if len(fakeResourceControl.deleteSlice) != 0 {
		t.Errorf("Expected no delete, but found one")
	}
}

func TestCleanupHistory(t *testing.T) {
	testCases := map[string]struct {
		useHistoryCountLimit       bool
		historyCountLimit          int32
		useHistoryTimeLimitSeconds bool
		historyTimeLimitSeconds    uint64
		existingActiveResources    []string
		existingFinishedResources  map[string]time.Time
		deletedResources           []string
	}{
		"no active and no finished resourced": {
			true,
			3,
			true,
			60,
			[]string{},
			map[string]time.Time{},
			[]string{},
		},
		"historyCountLimit and finished resources": {
			true,
			2,
			false,
			0,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Hour),
				"fr2": baseTime.Add(-9 * time.Hour),
				"fr3": baseTime.Add(-8 * time.Hour),
				"fr4": baseTime.Add(-7 * time.Hour),
				"fr5": baseTime.Add(-6 * time.Hour),
			},
			[]string{"fr1", "fr2", "fr3"},
		},
		"historyCountLimit and fewer finished resources": {
			true,
			5,
			false,
			0,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Hour),
				"fr2": baseTime.Add(-9 * time.Hour),
				"fr3": baseTime.Add(-8 * time.Hour),
				"fr4": baseTime.Add(-7 * time.Hour),
				"fr5": baseTime.Add(-6 * time.Hour),
			},
			[]string{},
		},
		"historyTimeLimit and finished resources": {
			false,
			0,
			true,
			(6 * 60) + 30,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Minute),
				"fr2": baseTime.Add(-9 * time.Minute),
				"fr3": baseTime.Add(-8 * time.Minute),
				"fr4": baseTime.Add(-7 * time.Minute),
				"fr5": baseTime.Add(-6 * time.Minute),
			},
			[]string{"fr1", "fr2", "fr3", "fr4"},
		},
		"historyCountLimit and no finished resources old enough": {
			false,
			0,
			true,
			12 * 60,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Minute),
				"fr2": baseTime.Add(-9 * time.Minute),
				"fr3": baseTime.Add(-8 * time.Minute),
				"fr4": baseTime.Add(-7 * time.Minute),
				"fr5": baseTime.Add(-6 * time.Minute),
			},
			[]string{},
		},
		"no historyCountLimit or historyTimeLimit should not delete any": {
			false,
			0,
			false,
			0,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Second),
				"fr2": baseTime.Add(-9 * time.Second),
				"fr3": baseTime.Add(-8 * time.Second),
				"fr4": baseTime.Add(-7 * time.Second),
				"fr5": baseTime.Add(-6 * time.Second),
			},
			[]string{},
		},
		"both historyCountLimit and historyTimeLimit, many old resources": {
			true,
			6,
			true,
			3 * 60,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Minute),
				"fr2": baseTime.Add(-9 * time.Minute),
				"fr3": baseTime.Add(-8 * time.Minute),
				"fr4": baseTime.Add(-7 * time.Minute),
				"fr5": baseTime.Add(-6 * time.Minute),
			},
			[]string{"fr1", "fr2", "fr3", "fr4", "fr5"},
		},
		"both historyCountLimit and historyTimeLimit, many new resources": {
			true,
			1,
			true,
			3 * 60,
			[]string{"ar1", "ar2", "ar3"},
			map[string]time.Time{
				"fr1": baseTime.Add(-10 * time.Second),
				"fr2": baseTime.Add(-9 * time.Second),
				"fr3": baseTime.Add(-8 * time.Second),
				"fr4": baseTime.Add(-7 * time.Second),
				"fr5": baseTime.Add(-6 * time.Second),
			},
			[]string{"fr1", "fr2", "fr3", "fr4"},
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

			ca := newFakeCronAnything(apiVersion, kind, namespace, name)
			ca.Spec.FinishableStrategy = &cronanything.FinishableStrategy{
				Type: cronanything.FinishableStrategyTimestampField,
				TimestampField: &cronanything.TimestampFieldStrategy{
					FieldPath: `{.status.finishedAt}`,
				},
			}
			ca.Spec.Retention = &cronanything.ResourceRetention{
				ResourceTimestampStrategy: cronanything.ResourceTimestampStrategy{
					Type: cronanything.ResourceTimestampStrategyField,
					FieldResourceTimestampStrategy: &cronanything.FieldResourceTimestampStrategy{
						FieldPath: `{.status.finishedAt}`,
					},
				},
			}
			if tc.useHistoryCountLimit {
				ca.Spec.Retention.HistoryCountLimit = &tc.historyCountLimit
			}
			if tc.useHistoryTimeLimitSeconds {
				ca.Spec.Retention.HistoryTimeLimitSeconds = &tc.historyTimeLimitSeconds
			}
			T := true
			ca.Spec.Suspend = &T
			fakeCronAnythingControl.getCronAnything = ca

			var resourceListResult []*unstructured.Unstructured
			for _, n := range tc.existingActiveResources {
				resourceListResult = append(resourceListResult, newUnstructuredResource(ca, n))
			}
			for n, finishedTimestamp := range tc.existingFinishedResources {
				resource := newUnstructuredResource(ca, n)
				unstructured.SetNestedField(resource.Object, finishedTimestamp.Format(time.RFC3339), "status", "finishedAt")
				resourceListResult = append(resourceListResult, resource)
			}
			fakeResourceControl.listResult = resourceListResult

			_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}

			sort.Strings(tc.deletedResources)
			sort.Strings(fakeResourceControl.deleteSlice)
			if !reflect.DeepEqual(tc.deletedResources, fakeResourceControl.deleteSlice) {
				t.Errorf("Expected deletion of resources %s, but found %s", strings.Join(tc.deletedResources, ","), strings.Join(fakeResourceControl.deleteSlice, ","))
			}
		})
	}
}

func TestTotalResourceLimit(t *testing.T) {
	testCases := map[string]struct {
		totalResourceLimit            int32
		historyLimit                  int32
		existingActiveResourceCount   int
		existingFinishedResourceCount int
		created                       bool
	}{
		"total number under totalResourceLimit": {
			5,
			3,
			2,
			2,
			true,
		},
		"active over totalResourceLimit": {
			5,
			3,
			6,
			0,
			false,
		},
		"active at totalResourceLimit and some finished": {
			5,
			3,
			5,
			3,
			false,
		},
		"active plus finished inside limit over totalResourceLimit": {
			5,
			3,
			3,
			3,
			false,
		},
		"active and finished over totalResourceLimit, but finished over historyLimit": {
			5,
			1,
			2,
			10,
			true,
		},
		"no active and no finished resources": {
			1,
			4,
			0,
			0,
			true,
		},
		"totalResourceLimit is zero": {
			0,
			4,
			0,
			0,
			false,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

			ca := newFakeCronAnything(apiVersion, kind, namespace, name)
			ca.Spec.TotalResourceLimit = &tc.totalResourceLimit
			ca.Spec.FinishableStrategy = &cronanything.FinishableStrategy{
				Type: cronanything.FinishableStrategyTimestampField,
				TimestampField: &cronanything.TimestampFieldStrategy{
					FieldPath: `{.status.finishedAt}`,
				},
			}
			ca.Spec.Retention = &cronanything.ResourceRetention{
				ResourceTimestampStrategy: cronanything.ResourceTimestampStrategy{
					Type: cronanything.ResourceTimestampStrategyField,
					FieldResourceTimestampStrategy: &cronanything.FieldResourceTimestampStrategy{
						FieldPath: `{.status.finishedAt}`,
					},
				},
			}
			ca.Spec.Retention.HistoryCountLimit = &tc.historyLimit

			fakeCronAnythingControl.getCronAnything = ca

			var resourceListResult []*unstructured.Unstructured
			for i := 0; i < tc.existingActiveResourceCount; i++ {
				resourceListResult = append(resourceListResult, newUnstructuredResource(ca, fmt.Sprintf("activeResource%d", i)))
			}
			for i := 0; i < tc.existingFinishedResourceCount; i++ {
				resource := newUnstructuredResource(ca, fmt.Sprintf("finishedResource%d", i))
				unstructured.SetNestedField(resource.Object, baseTime.Format(time.RFC3339), "status", "finishedAt")
				resourceListResult = append(resourceListResult, resource)
			}
			fakeResourceControl.listResult = resourceListResult

			_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
			}

			didCreate := fakeResourceControl.createCount > 0
			if didCreate != tc.created {
				t.Errorf("Expected %t, but found %t", tc.created, didCreate)
			}
		})
	}
}

func TestCreateResourceAndUpdateCronAnything(t *testing.T) {
	reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()

	resourceTemplate := map[string]interface{}{
		"apiVersion": apiVersion,
		"kind":       kind,
		"spec": map[string]interface{}{
			"greeting":   "Hello World!",
			"greetCount": 42,
		},
	}
	resourceTemplateBytes, _ := json.Marshal(resourceTemplate)

	ca := &fakeCronAnything{
		ObjectMeta: metav1.ObjectMeta{
			CreationTimestamp: metav1.Time{
				Time: baseTime.Add(-60 * time.Second),
			},
			Name: "myCronAnything",
			UID:  "myUID",
		},
		Spec: cronanything.CronAnythingSpec{
			Schedule: defaultCronExpr,
			Template: runtime.RawExtension{
				Raw: resourceTemplateBytes,
			},
		},
	}
	fakeCronAnythingControl.getCronAnything = ca

	times, _, _ := getScheduleTimes(ca, baseTime)
	expectedTriggerTime := times[len(times)-1]

	_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
		NamespacedName: types.NamespacedName{
			Namespace: namespace,
			Name:      name,
		},
	})
	if err != nil {
		t.Errorf("%s: Unexpected error: %v", name, err)
	}

	createdResourceTemplate := fakeResourceControl.createTemplate.Object
	if !reflect.DeepEqual(createdResourceTemplate, fakeResourceControl.createTemplate.Object) {
		t.Error("Expected template and created resource to be equal, but they weren't")
	}

	lastScheduleTime := fakeCronAnythingControl.updateCronAnything.CronAnythingStatus().LastScheduleTime.Time
	if expectedTriggerTime != lastScheduleTime {
		t.Errorf("Expected %s, but got %s", expectedTriggerTime.Format(time.RFC3339), lastScheduleTime.Format(time.RFC3339))
	}
}

func TestGetResourceName(t *testing.T) {
	timestamp := toTimestamp(t, "2012-11-01T22:08:41+00:00")

	testCases := map[string]struct {
		cronAnything cronanything.CronAnything
		scheduleTime time.Time
		expectedName string
	}{
		"default name, default timestamp format": {
			cronAnything: newCronAnythingForResourceName("myresource", nil, nil),
			scheduleTime: timestamp,
			expectedName: "myresource-1351807721",
		},
		"custom name, default timestamp format": {
			cronAnything: newCronAnythingForResourceName("myresource", toPointer("anotherResource"), nil),
			scheduleTime: timestamp,
			expectedName: "anotherResource-1351807721",
		},
		"default name, custom timestamp format": {
			cronAnything: newCronAnythingForResourceName("myresource", nil, toPointer("20060102150405")),
			scheduleTime: timestamp,
			expectedName: "myresource-20121101220841",
		},
		"custom name, custom timestamp format": {
			cronAnything: newCronAnythingForResourceName("myresource", toPointer("anotherResource"), toPointer("2006-01-02-15-04-05")),
			scheduleTime: timestamp,
			expectedName: "anotherResource-2012-11-01-22-08-41",
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			name := getResourceName(tc.cronAnything, tc.scheduleTime)
			if tc.expectedName != name {
				t.Errorf("expected %s, but got %s", tc.expectedName, name)
			}
		})
	}
}

func TestTriggerHistory(t *testing.T) {
	testCases := map[string]struct {
		CurrentTime            time.Time
		TriggerDeadline        *int64
		ConcurrencyPolicy      cronanything.ConcurrencyPolicy
		ExistingResourceCount  int
		CurrentStatus          cronanything.CronAnythingStatus
		CreateResourceError    error
		ReconcileFails         bool
		ExpectedTriggerHistory []cronanything.TriggerHistoryRecord
		ExpectedPendingTrigger *cronanything.PendingTrigger
	}{
		"Successful create added to history": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 20, 01, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 19, 0, 0, time.UTC)),
			},
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateSucceeded,
				},
			},
		},
		"Create fails first time": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 20, 01, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 19, 0, 0, time.UTC)),
			},
			CreateResourceError: errors.New("this is a test error"),
			ReconcileFails:      true,
			ExpectedPendingTrigger: &cronanything.PendingTrigger{
				ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
				Result:       cronanything.TriggerResultCreateFailed,
			},
		},
		"Create fails second time": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 20, 05, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 19, 0, 0, time.UTC)),
				PendingTrigger: &cronanything.PendingTrigger{
					ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 00, 0, time.UTC)),
					Result:       cronanything.TriggerResultCreateFailed,
				},
			},
			CreateResourceError: errors.New("this is a test error"),
			ReconcileFails:      true,
			ExpectedPendingTrigger: &cronanything.PendingTrigger{
				ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
				Result:       cronanything.TriggerResultCreateFailed,
			},
		},
		"New trigger time reached while create fails": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 19, 0, 0, time.UTC)),
				PendingTrigger: &cronanything.PendingTrigger{
					ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 00, 0, time.UTC)),
					Result:       cronanything.TriggerResultCreateFailed,
				},
			},
			CreateResourceError: errors.New("this is a test error"),
			ReconcileFails:      true,
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateFailed,
				},
			},
			ExpectedPendingTrigger: &cronanything.PendingTrigger{
				ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
				Result:       cronanything.TriggerResultCreateFailed,
			},
		},
		"New trigger time reached with create failed and new succeeds": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 19, 0, 0, time.UTC)),
				PendingTrigger: &cronanything.PendingTrigger{
					ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 00, 0, time.UTC)),
					Result:       cronanything.TriggerResultCreateFailed,
				},
			},
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateSucceeded,
				},
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateFailed,
				},
			},
		},
		"Multiple triggers missed with no pending trigger": {
			CurrentTime: time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 10, 0, 0, time.UTC)),
			},
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateSucceeded,
				},
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultMissed,
				},
			},
		},
		"Trigger deadline exceeded without pending trigger": {
			CurrentTime:     time.Date(2018, time.April, 20, 4, 21, 31, 0, time.UTC),
			TriggerDeadline: func(n int64) *int64 { return &n }(30),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
			},
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 31, 0, time.UTC)),
					Result:            cronanything.TriggerResultDeadlineExceeded,
				},
			},
		},
		"Trigger deadline exceeded with pending trigger": {
			CurrentTime:     time.Date(2018, time.April, 20, 4, 21, 31, 0, time.UTC),
			TriggerDeadline: func(n int64) *int64 { return &n }(30),
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
				PendingTrigger: &cronanything.PendingTrigger{
					ScheduleTime: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					Result:       cronanything.TriggerResultCreateFailed,
				},
			},
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 31, 0, time.UTC)),
					Result:            cronanything.TriggerResultCreateFailed,
				},
			},
		},
		"Forbid concurrent policy with existing unfinished resource": {
			CurrentTime:       time.Date(2018, time.April, 20, 4, 21, 1, 0, time.UTC),
			ConcurrencyPolicy: cronanything.ForbidConcurrent,
			CurrentStatus: cronanything.CronAnythingStatus{
				LastScheduleTime: getMetaTimePointer(time.Date(2018, time.April, 20, 4, 20, 0, 0, time.UTC)),
			},
			ExistingResourceCount: 1,
			ExpectedTriggerHistory: []cronanything.TriggerHistoryRecord{
				{
					ScheduleTime:      metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 0, 0, time.UTC)),
					CreationTimestamp: metav1.NewTime(time.Date(2018, time.April, 20, 4, 21, 01, 0, time.UTC)),
					Result:            cronanything.TriggerResultForbidConcurrent,
				},
			},
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			reconciler, fakeCronAnythingControl, fakeResourceControl := createReconciler()
			reconciler.currentTime = func() time.Time { return tc.CurrentTime }

			ca := newFakeCronAnything(apiVersion, kind, namespace, name)
			ca.CreationTimestamp = metav1.NewTime(tc.CurrentTime.Add(-time.Hour))
			ca.Spec.Schedule = defaultCronExpr
			ca.Spec.TriggerDeadlineSeconds = tc.TriggerDeadline
			ca.Spec.ConcurrencyPolicy = tc.ConcurrencyPolicy
			ca.Status = tc.CurrentStatus

			fakeCronAnythingControl.getCronAnything = ca

			fakeResourceControl.listResult = createUnstructuredSlice(ca, tc.ExistingResourceCount)
			fakeResourceControl.createError = tc.CreateResourceError

			_, err := reconciler.Reconcile(context.Background(), reconcile.Request{
				NamespacedName: types.NamespacedName{
					Namespace: namespace,
					Name:      name,
				},
			})
			if !tc.ReconcileFails && err != nil {
				t.Errorf("Expected reconcile to succeed, but it failed with error %v", err)
			}
			if tc.ReconcileFails && err == nil {
				t.Errorf("Expected reconcile to fail, but it didn't")
			}

			status := fakeCronAnythingControl.updateCronAnything.CronAnythingStatus()
			if !isTriggerHistoriesEqual(status.TriggerHistory, tc.ExpectedTriggerHistory) {
				t.Errorf("Expected %v in trigger history, but found %v", tc.ExpectedTriggerHistory, status.TriggerHistory)
			}

			if !reflect.DeepEqual(status.PendingTrigger, tc.ExpectedPendingTrigger) {
				t.Errorf("Expected %v as pending trigger, but found %v", tc.ExpectedPendingTrigger, status.PendingTrigger)
			}
		})
	}
}

func TestTriggerHistoryLength(t *testing.T) {
	existingRecordsStatus := cronanything.TriggerResultCreateSucceeded
	newRecordStatus := cronanything.TriggerResultCreateFailed

	testCases := map[string]struct {
		initialHistoryLength int
		finalHistoryLength   int
	}{
		"new record to empty history": {
			initialHistoryLength: 0,
			finalHistoryLength:   1,
		},
		"new record to full history": {
			initialHistoryLength: 10,
			finalHistoryLength:   10,
		},
	}

	for tn, tc := range testCases {
		t.Run(tn, func(t *testing.T) {
			var history []cronanything.TriggerHistoryRecord
			for i := 0; i < tc.initialHistoryLength; i++ {
				history = append(history, cronanything.TriggerHistoryRecord{
					ScheduleTime:      metav1.NewTime(time.Now()),
					CreationTimestamp: metav1.NewTime(time.Now()),
					Result:            existingRecordsStatus,
				})
			}
			status := &cronanything.CronAnythingStatus{
				TriggerHistory: history,
			}

			r := cronanything.TriggerHistoryRecord{
				ScheduleTime:      metav1.NewTime(time.Now()),
				CreationTimestamp: metav1.NewTime(time.Now()),
				Result:            newRecordStatus,
			}
			addToTriggerHistory(status, r)

			if got, want := len(status.TriggerHistory), tc.finalHistoryLength; got != want {
				t.Errorf("Expected trigger history to have %d record, but found %d", want, got)
			}

			headOfHistory := status.TriggerHistory[0]
			if headOfHistory.Result != newRecordStatus {
				t.Errorf("Expected new record to be first in the history, but it was't")
			}
		})
	}
}

func isTriggerHistoriesEqual(actualHistory []cronanything.TriggerHistoryRecord, expectedHistory []cronanything.TriggerHistoryRecord) bool {
	if len(actualHistory) != len(expectedHistory) {
		return false
	}
	for index, actual := range actualHistory {
		expected := expectedHistory[index]
		if expected.Result != actual.Result || !expected.ScheduleTime.Equal(&actual.ScheduleTime) || !expected.CreationTimestamp.Equal(&actual.CreationTimestamp) {
			return false
		}
	}
	return true
}

func createUnstructuredSlice(ca *fakeCronAnything, count int) []*unstructured.Unstructured {
	var unstructuredSlice []*unstructured.Unstructured
	for i := 0; i < count; i++ {
		unstructuredSlice = append(unstructuredSlice, newUnstructuredResource(ca, fmt.Sprintf("resource-%d", i)))
	}
	return unstructuredSlice
}

func newCronAnythingForResourceName(name string, resourceBaseName, resourceTimestampFormat *string) cronanything.CronAnything {
	ca := newFakeCronAnything("db.anthosapis.com/v1alpha1", "TestKind", name, "default")
	ca.Spec.ResourceBaseName = resourceBaseName
	ca.Spec.ResourceTimestampFormat = resourceTimestampFormat
	return ca
}

func toPointer(s string) *string {
	return &s
}

func toTimestamp(t *testing.T, timestampString string) time.Time {
	timestamp, err := time.Parse(time.RFC3339, timestampString)
	if err != nil {
		t.Fatal(err)
	}
	return timestamp
}

type fakeCronAnythingControl struct {
	getKey          client.ObjectKey
	getCronAnything cronanything.CronAnything
	getError        error

	updateCronAnything cronanything.CronAnything
	updateError        error
}

func (r *fakeCronAnythingControl) Get(key client.ObjectKey) (cronanything.CronAnything, error) {
	r.getKey = key
	return r.getCronAnything, r.getError
}

func (r *fakeCronAnythingControl) UpdateStatus(ca cronanything.CronAnything) error {
	r.updateCronAnything = ca
	return r.updateError
}

func (r *fakeCronAnythingControl) Create(ns string, name string, cas cronanything.CronAnythingSpec, owner cronanything.BackupSchedule) error {
	return nil
}

type fakeResourceControl struct {
	createResource  schema.GroupVersionResource
	createNamespace string
	createTemplate  *unstructured.Unstructured
	createCount     int
	createError     error

	deleteSlice []string
	deleteError error

	listResult []*unstructured.Unstructured
	listError  error
}

func (r *fakeResourceControl) Delete(resource schema.GroupVersionResource, namespace, name string) error {
	r.deleteSlice = append(r.deleteSlice, name)
	return r.deleteError
}

func (r *fakeResourceControl) Create(resource schema.GroupVersionResource, namespace string, template *unstructured.Unstructured) error {
	r.createResource = resource
	r.createNamespace = namespace
	r.createTemplate = template
	r.createCount += 1
	return r.createError
}

func (r *fakeResourceControl) List(resource schema.GroupVersionResource, _ string) ([]*unstructured.Unstructured, error) {
	return r.listResult, r.listError
}

type fakeResourceResolver struct {
}

func (r *fakeResourceResolver) Start(interval time.Duration, stopCh <-chan struct{}, log logr.Logger) {
}

func (r *fakeResourceResolver) Resolve(gvk schema.GroupVersionKind) (schema.GroupVersionResource, bool) {
	return schema.GroupVersionResource{}, true
}

redpajama_set_name: RedPajamaGithub


text:
The Curious World Of Strange and Possibly Illegal Celebrity Pets
Celebrities are used to making international headlines. Whether they're catching fans' attention with news about a upcoming movie or project or are shocking the world with…
Did You Know It's Possible To Make Perfume From Extinct Flowers?
Smell is one of the most powerful senses we have and can trigger all kinds of emotions and memories. What would it be like the smell…
Would You Eat Bread Made From 4,500-Year Old Yeast?
The process of baking bread, especially sourdough, is kind of weird when you really think about it. Yeast, which makes the bread rise, is essentially tiny…
The Deep, Dark Secrets Disney is Keeping Under Wraps
The Walt Disney Company is one of the biggest and most far-reaching corporations in the world. With hundreds of films and TV shows beloved by children…
In 2020, Wendy's Will Finally Take On Breakfast
Fast food breakfasts have been all the rage with McDonald's and Taco Bell offering expanded menus, but Wendy's has remained quiet for a long time. It…
Are Salt Lamps Legit Or A Scam?
If you're into health and wellness at all, you've probably heard about pink Himalayan salt. It's supposedly packed with healthy minerals that regular salt lacks, and…
Can Virtual Reality Transform How We See Autism?
Autism affects people differently, but certain traits tend to appear, like challenges with social skills, verbal and non-verbal communication, attention, and so on. Depending on the…
Adulting 101—Essential Survival Skills
It is no secret that it is a big ol scary world out there—and it is getting bigger and scarier more and more every day. That…
Miniature Horses Now Clear To Travel On Airlines
In a day and age where service animals come in all types, forms, and species, this one is admittedly a little outside the norm. Last week,…
Could Cockroach Milk Save The World?
Have you ever looked at a cockroach scuttling under your fridge and wondered, "I wonder if I could milk that?" Apparently, somebody did, because in 2016…

redpajama_set_name: RedPajamaCommonCrawl


CHHS offers graduate and undergraduate degrees that combine the Jesuit liberal arts with unmatched professional practice and teaching expertise.
The College of Health and Human Services offers graduate and undergraduate programs in the health sciences, physics and engineering. As a CHHS graduate, you can expect to find success in your chosen field, as well as in your personal life. Here, you'll become a leader for life. A leader committed to learning, leadership and service.
Our programs combine the skills you need to succeed in today's workplace, with learning experiences that teach you how to think, not what to think.
Why Earn a Degree from CHHS at RU?
A degree from CHHS at RU not only includes evidence-based curricular content but is built upon active-learning techniques including simulation, hands-on activities, and service learning.
Each program offers a unique set of opportunities that encourage students to apply the concepts learned in the classroom with the real world. Our programs have partnerships with clinical sites, schools, and other community facilities for our students to take their learning to a new level. Several of these allow for student experiences outside of the Kansas City area providing a more enriching and diverse experience. International experiences are available for some programs in CHHS.
Each of our graduate programs were awarded full accreditation from their respective professional accrediting agency.
We aspire to transform and strengthen the learning, health and well-being of our dynamic society.
As a learner-centered, service and community-minded college, we develop reflective leaders in health and human services who are ethical, inclusive and compassionate professionals.
Each year, the College hosts a speaker on campus to discuss issues related to current trends in health or human services.
On Sept. 20, 2018, Rockhurst faculty from a variety of disciplines will participate in a panel discussion around the causes and treatment of concussions. Following the panel, breakout sessions will focus on clinical and K6 classroom care for concussions. PTs, OTs, SLPs and Educators are invited to attend.
On Sept. 28, 2017, Holly Godfrey spoke about her role as founder and CEO of Catalyst Scrubs – a fair trade medical apparel company that partners with women in need around the world to provide sustainable, fair-wage jobs.
On Sept. 29, 2016, Dr. Cole Galloway, physical therapist, neuroscientist and founder of the GoBabyGo! movement, discussed emerging technology he hopes will empower people to create their own world through mobility.


text:
Osman Nuhu Sharubutu (Accra, 23 april 1919) is een Ghanees imam en een islamitische geleerde.

Levensloop

Biografie
Sharubutu is geboren in de sloppenwijk Old Fadama in het centrum van Accra. Zijn ouders waren Nuhu Sharubutu en Hajja Ayishatu Abbass, handelaren van Hausa afkomst uit het noorden van Nigeria. Zijn ouders onderwezen Sharubutu thuis, maar in zijn tienerjaren vertrok hij naar Kumasi, waar hij onder andere de Arabische literatuur, de Hadiths en de islamitische jurisprudentie bestudeerde. Na zijn studie bleef hij in Kumasi wonen en begon als leraar op een koranschool werken.

Carrière
In 1974 werd Sharubutu voorgedragen als de 'regionale hoofdimam' van Ghana, maar dit aanbod weigerde hij vanwege druk van hooggeplaatste islamitische stamleiders. In 1993 werd Sharubutu benoemd tot 'hoofdimam' van Ghana. Zijn voornaamste doel is om een interreligieuze dialoog en om een vreedzaam samenleven tussen moslims en christenen, de twee dominante religieuze groepen in Ghana, te bevorderen.

De voormalige president van Ghana, John Agyekum Kufuor, beschreef Sharubutu als een wijze man die de Nobelprijs voor de Vrede verdient voor het opkomen voor 'vredesopbouw', niet alleen in Ghana, maar ook in andere delen van het Afrikaanse continent.

Op 16 juli 2021, bij de opening van de op de Blauwe Moskee gebaseerde Nationale Moskee van Ghana (in Accra), verzorgde Sharubutu op 102-jarige leeftijd het vrijdaggebed. Onder de aanwezigen waren onder andere de Ghanese president Nana Akufo-Addo, de Nigerese politici Mohamed Bazoum en Mahamadou Issoufou, de Turkse vicepresident Fuat Oktay en de voorzitter van Diyanet, Ali Erbaş.

Islamitisch geestelijke
Ghanees geestelijke
Honderdplusser

redpajama_set_name: RedPajamaWikipedia


text:
<?php
namespace Janitor\Abstracts;

use Janitor\Entities\View;
use Janitor\TestCases\JanitorTestCase;
use Symfony\Component\Finder\SplFileInfo;

class AbstractAnalyzedEntityTest extends JanitorTestCase
{
	public function testCanGetUsageMatrixAsString()
	{
		$view = $this->getDummyEntity();

		$pattern = strtr('{folder}/{file}|{folder}/{basename}|{dotfolder}.{basename}|{file}|{basename}', array(
			'{folder}'    => __DIR__,
			'{dotfolder}' => str_replace(DS, '.', __DIR__),
			'{file}'      => basename(__FILE__),
			'{basename}'  => str_replace('.php', null, basename(__FILE__)),
		));

		$this->assertEquals($pattern, $view->getUsagePattern());
	}

	public function testCanTransformToArray()
	{
		$view = $this->getDummyEntity();

		$array = $view->toArray();
		$this->assertInternalType('array', $array);
		$this->assertEquals('folder', $array['root']);
	}

	public function testCanTransformToJson()
	{
		$view = $this->getDummyEntity();

		$json = json_encode($view);
		$this->assertInternalType('string', $json);
		$this->assertContains('{"root":"folder"', $json);
	}

	/**
	 * @return View
	 */
	protected function getDummyEntity()
	{
		$file = new SplFileInfo(__FILE__, __DIR__, __DIR__.'/'.__FILE__);
		$view = new View('folder', $file->getBasename());
		$view->setFile($file);

		return $view;
	}
}


Join us for an interstellar evening at Out Loud HSV's Paperback Prom: Out of This Word! On April 28th at 7pm get dressed up and join us in the Flying Monkey Theater for music, raffles, snack and punch, photo opportunities, open mics and more, all to help raise funds for the 2018 Out Loud HSV Poetry Slam Team.
We will have two open mics - the first will be Space Themed at 7:30pm, and our Dramatic Recitations of Prom Songs open mic will follow. Sign up and perform for one or both for the chance to be crowned our 'Prom Person'.
Dance under the stars to music, enter the raffle to win wonderful local goodies, and enjoy food & cosmic punch!
Need a new outfit for the occasion? Visit our incredible sponsor Karma Rags and mention the Out Loud HSV prom for a discount on your outfit!
Green Pea Press will be on site printing up souvenir shirts for the evening so get them while you can!


Cut and shut to 30ft.
Single high security personnel door fitted.
Severing hatch fitted with manual roller shutter.
Emergency exit personnel door fitted with vents either side.
Stainless steel cooking hood fitted with extraction fan and housing box and louvered vent.


text:
\section{Introduction}

\begin{figure*}
\centering
\includegraphics[width=\textwidth]{figs/HST_image.eps}
\caption{Left panel: {\it HST-NICMOS 2} image of the BR1202-0725 system, using the F160W filter, covering the SINFONI FoV of $8\arcsec \times 8\arcsec$. White crosses mark the positions of the SMG, quasar, Ly$\alpha1$ and Ly$\alpha2$ as labeled, according to \citet{carilli13}. White squares show the regions where spectra were extracted from the SINFONI datacube. Right panel: {\it HST-WFC3} image of J1000+0234, using the IR channel and F160W filter. White squares show the regions where SMG spectra were extracted from the SINFONI datacube, as identified. The white arrow points to a foreground object.}
\label{hst}
\end{figure*}

Recent studies of galaxies detected at millimeter and submillimeter wavelengths have largely increased our understanding of the formation and evolution of massive galaxies when the Universe was 1-3 Gyrs old. Cosmological simulations indicate that massive galaxies can form at high-$z$ via gas-rich mergers, triggering extreme events such as intense star formation and simultaneously growing of supermassive black holes (SMBHs) close to their maximum (Eddington) accretion rates \citep{li07,netzer14}. Submillimeter galaxies (SMGs) play a key role in this scenario, since they represent examples of starburst galaxies (above main sequence) in the distant universe. At such high redshifts ($z\sim5$), a large fraction of the star formation activity is enshrouded in dust, most of the bolometric luminosity is radiated into the far-infrared (FIR, $40-500\,\mu$m) and submillimeter wavelengths and therefore the luminosity of galaxies is proportional to the star formation rate (SFR). SMGs observed with {\it Herschel} and ALMA \citep{mor12,vieira13,netzer14} have displayed extremely high infrared luminosities ($L_{FIR} \geq 10^{13} L_\odot$), which implies SFRs of $> 10^3 M_\odot\,$year$^{-1}$. These rates indicate that the bulk of the star formation in these galaxies have timescales of only $\leqslant 100\,$Myr.

The structure and physical mechanisms at work in these objects are largely unknown. Different scenarios have been proposed that imply the removal of the available gas due to an active galactic nucleus (AGN) driven wind in luminous quasars or to supernovae and stellar winds in extreme starbursts, leaving behind a compact remnant \citep{sanders88,hopkins08,wuyts10}. However, evidence for significant outflow rates at high-$z$ are very limited and have only recently been achieved. One example is the highly magnified galaxy at $z = 4.92$ behind the lensing cluster MS 1358+62 \citep{swinbank09}, in which deep [O\,{\sc ii}]$\lambda\lambda$3726,29\AA\, spectroscopy indicates the presence of a young outflow ($< 15\,$Myr) and a SFR of $42$ $\rm M_\odot\,yr^{-1} \,$.

In this work, we present deep SINFONI--IFS data of two $z \sim 5$ SMGs, BR 1202-0725 and COSMOS J100054+023436. BR 1202-0725 ($z = 4.69$) is a system composed by a dusty, luminous starburst (the SMG itself), an optically luminous QSO and two Ly$\alpha$ emitting extented regions, hereafter Ly$\alpha1$ and Ly$\alpha2$ \citep{omont96,carilli02}. The QSO and the SMG are separated by $3\farcs8$, with Ly$\alpha1$ between them ($2\farcs3\,$ north-west of the QSO), a clear signature of interaction, while Ly$\alpha2$ lies $2\farcs7\,$ south-west of the QSO (see left panel of Figure \ref{hst}). Recent observations of the system using ALMA detects narrow [C\,{\sc ii}]$\lambda158\,\mu$m emission in all four sources \citep{wagg12,carilli13}. Both SMG and QSO have high FIR luminosity ($L_{FIR} > 10^{13} L_\odot$) indicating SFRs of the order of $10^3\,$$\rm M_\odot\,yr^{-1} \,$ or above \citep{iono06,carniani13}, while both Ly$\alpha$ emitting regions, Ly$\alpha1$ and Ly$\alpha2$, appear to be forming stars, added together, at a rate of $19$ $\rm M_\odot\,yr^{-1} \,$ \citep{williams14}, derived from the [C\,{\sc ii}] $158 \mu$m emission-line luminosities. \citet{carilli13} concluded that the proximity of a luminous quasar is unlikely to be the source of the ionized nebula in Ly$\alpha1$, and speculate that a optically thick torus is shielding the radiation towards the Lyman-alpha emitters (LAEs, i.e. Ly$\alpha$1 and Ly$\alpha$2). Gas outflows related to the QSO are under debate, with some evidence based on the detection of a broad [C\,{\sc ii}] secondary component \citep{carilli13}, not confirmed by similar subsequent studies \citep{carniani13}. \citet{ohta00} report detection of [O\,{\sc ii}] doublet emission of the Ly$\alpha1\,$ galaxy based on narrow-band imaging.

COSMOS J100054+023436 (hereafter J1000+0234, $z = 4.55$) is a SMG dominated by a starburst forming stars at a rate of $> 1000\,$$\rm M_\odot\,yr^{-1} \,$ with a young age of $2-8\,$ Myr, estimated using infrared and radio measurements \citep{capak08}. This SMG presents multiple components observed in the Ly$\alpha$ emission-line, distributed along a region of $\approx 3''$ in size, and residing at the same redshift. This galaxy presents different morphology at different wavelengths, from the UV (Ly$\alpha$) to the near-infrared (rest-frame optical with [O\,{\sc ii}] doublet), likely indicating the presence of spatially resolved stellar populations on arcsec scales \citep{capak08}. Detection of a broad CO($4-3$) emission line indicates the presence of a large amount of molecular gas with an estimated total mass of $2.6 \times 10^{10}\,\textrm{M}_\odot$. The width of the CO($4-3$) line and the highly disturbed morphology suggest this system is involved in an ongoing merger \citep{schinnerer08}.

The objective of this work is to search the [O\,{\sc ii}]$\lambda\lambda$3726,29\AA\, doublet in these two SMGs, to study the structure of the un-obscured star formation, including the presence of flows of gas other than rotation. This paper is organized as follows: in Sec. \ref{obs} we describe the observations and data reduction, in Sec. \ref{res} we present the results from the SINFONI datacubes for both SMG systems. In Sec. \ref{analys} we discuss the implications for the derived star formation rate, internal extinction in the visual and the claim of previous [O\,{\sc ii}] detection in the Ly$\alpha1$ of the BR1202-0725 system. Finally, conclusions are summarized in Sec. \ref{conc}. Thoughout this paper we assume a standard concordance cosmology ($H_0 = 70$, $\Omega_M = 0.3$, $\Omega_\Lambda = 0.7$).

\section{Observations and data reduction}
\label{obs}

The data were obtained in service mode between January and July 2015 using the near-IR integral field spectrograph SINFONI on the VLT (period 93A). The objects were observed in the {\it K} band (1.95-2.45 $\mu$m), with each exposure sliced into 32 slitlets imaged onto 64 pixels of the detector, leading to a spatial scale of $0\farcs125 \times 0\farcs250\,$pixel$^{-1}$. We used different observation strategies for the two objects. For BR1202-0725, 15 independent Observing Blocks (OB), each with an on-source + sky pattern given by the O-S-S-O-O-S sequence were executed. The total on-source exposure time is $3.7\,$h, and the field of view (FoV) is $\approx 10'' \times 8''$. For J1000+0234, 2 on-source exposures of 600\,s each were obtained in 15 OBs (except for one OB where exposures of 300\,s were taken), following an O-S-S-O pattern for on-source and sky frames. In this case, since the object is compact and smaller than the field-of-view of SINFONI, the sky exposures were obtained $3\arcsec$ east of the on-source pointing, thus also containing the object within SINFONI FoV. The resulting total on-source exposure time is then $9.7\,$h, with a total FoV of $\approx 14'' \times 8''$.

The spectral resolution for the {\it K} band is $R \sim 3100-3200$, and the full width at half maximum (FWHM) measured from the OH sky lines is ($6.9\pm0.3\,$\AA), with a dispersion of $2.45\,$\AA/pixel. Aside from respective sky frames exposures, a set of spectrophotometric standard stars were also observed, to correct the data for instrument response and for flux calibration. Reduction process was perfomed using the standard ESO pipeline ESOREX (version 3.10.2), and own IDL routines for flux calibration. The resulted calibration present flux uncertainties of $\approx 6\%\,$ ($1\,\sigma$) for the absolute flux, estimated as the standard deviation of the flux factors from the different calibration stars (17 and 14 standard stars observed for BR1202-0725 and J1000+0234, respectively). Individual frames were corrected for dark subtraction, flat-fielding, detector linearity, geometrical distortion and were wavelength calibrated. We have applied two different methods to address the emission due to the sky (both lines and thermal continuum). We applied the standard method of subtracting the closest sky frame to each target frame for each Observing Block (OB), and combining the individual sky subtracted cubes per OB into a single final data cube. The results are indicated by the magenta line in Fig. \ref{sky_sub} for BR1202-0725. Since the redshifted [O\,{\sc ii}] lines were expected to land in between the sky OH lines, an average sky thermal continuum emission was generated with all sky frames. This was subtracted from the final data cube with the spectrum of the object. The end result is presented with the green line in Fig. \ref{sky_sub}. Since the noise level with this method is somewhat more conservative, i.e. higher noise, we decided to use this method and the corresponding residuals to place more conservative upper limits to the [O\,{\sc ii}] flux. Cubes were then constructed from the individual frames, and the several pointings were combined to build a final mosaic, with each spectrum in the cube corresponding to an area of $0\farcs125\, \times 0\farcs125$. To shift and register all individual cubes into a single final data cube, we used the right ascension and declination keywords in the header, since the informed offsets were not matching the relative shifts as measured by the position of the QSO (relative shifts of up to $0\farcs5$ were measured in a few cases). The accuracy of this procedure is given by the spatial size of a pixel ($0\farcs125$), since the coordinates information are related to the central pixel of each individual datacube. In the case of BR1202-0725, we have also derived a final sky datacube, averaging the individual sky frames. Our seeing-limited observations have an average angular resolution of $0\farcs71\pm0\farcs26\,$ and $0\farcs59\pm0\farcs09\,$ for BR1202-0725 and J1000+0234 respectively, obtained from observations of the accompanying standard stars and assuming a gaussian profile.

\section{Results}
\label{res}

\subsection{BR1202-0725}

\begin{figure}
\centering
\includegraphics[width=0.5\textwidth]{figs/plot_skysub.eps}
\caption{A comparison between the standard sky subtraction applied in the data reduction previous to combining the individual datacubes (magenta line) and the sky subtraction applied after constructing the final datacube (green line), for the SMG of BR1202-0725. The latter method is the one used in this work. The wavelengths of the [O\,{\sc ii}] doublet are shown in vertical dashed black lines and dotted vertical lines indicate sky lines.}
\label{sky_sub}
\end{figure}

\begin{figure*}
\centering
\includegraphics[width=\textwidth]{figs/spec_BR.eps}
\caption{Black lines: Integrated spectrum corresponding to the [O\,{\sc ii}] doublet range for the quasar, SMG, Ly$\alpha1$ and Ly$\alpha2$ (from top to bottom) of the BR1202-0725 system, extracted from the square apertures shown in the left panel of Fig. \ref{hst}, shifted by a constant ($2 \times 10^{-16}\,$$\rm erg\,s^{-1}\,\,cm^{-2}\,\AA^{-1} \,$), added for visual purposes. Red lines: Sky spectra, which were derived as the median of the sky spectra extracted from the same apertures shown in Fig. \ref{hst}. Green lines show the residual between object and sky spectra and 1$\sigma$ uncertainty of the sky spectra are displayed in blue lines. Gray shaded areas display the 5$\sigma$ level. The wavelengths of the [O\,{\sc ii}] doublet considering [C\,{\sc ii}] redshifts \citep{carilli13} are shown in vertical dashed black lines. Dash-dotted lines show the wavelengths from Ly$\alpha$ redshifts \citep{ohyama04} for Ly$\alpha1$ (see Table \ref{redshift}). Dotted vertical lines indicate sky lines.}
\label{spec_br}
\end{figure*}

The entire system consisting of the SMG, a QSO and two Ly$\alpha$ emitters is shown in left panel of Figure \ref{hst}. The figure displays a {\it HST-NICMOS 2} image in the F160W filter (central wavelength of $1.60\,\mu$m) of the BR1202-0725 system within the same FoV of SINFONI, along with the positions of the four components of the system marked by a white cross, except for the quasar that is clearly visible. The positions were obtained from \citet{carilli13}, based on submillimeter continuum measurements, with the exception of Ly$\alpha1$, where the peak of the [C\,{\sc ii}] line emission was used. The SMG and the Ly$\alpha$ emitters are not detected in this image. White squares correspond to $0\farcs75 \times 0\farcs75$ apertures where spectra were extrated from the SINFONI data cube. The size of the apertures matches the seeing (FWHM).

The resulting extracted spectra are displayed in Fig. \ref{spec_br}. The corresponding flux limits for the two [O\,{\sc ii}] lines of the doublet are given in Table \ref{redshift} for the different components of the BR1202-0725 system assuming redshifts given by the [C\,{\sc ii}]$158\,\mu$m lines. To estimate the fluxes given in Table \ref{redshift}, we assumed that the emission lines were unresolved at the spectral resolution provided by SINFONI, and therefore considered a width of $6.9\,$\AA (FWHM). For Ly$\alpha1$ flux estimates we assume the [O\,{\sc ii}] in emission is at the redshift of the Ly$\alpha$ line. The spectra of the SMG and LAEs in the BR 1202-0725 field display no evidence of [O\,{\sc ii}] in emission above the (5$\sigma$) sky background uncertainties with upper limits $\sim$ 0.8-1.5 $\times$ 10$^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$ for the combined flux of the doublet. The only clear features in the spectra of both the objects and are the sky lines identified by blue dotted lines. For the QSO associated with the system, there is a significant detection of the continuum but no evidence of the [O\,{\sc ii}] emission line, given the upper flux limit corresponding to $\sim$ 9 $\times$ 10$^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$.

\begin{table*
   \centering
   \caption{\it Redshifts and measured fluxes for the objects of the BR1202-0725 and J1000+0234 systems.}
   \begin{tabular}{c c c c c}
      \hline \hline
      Object & Emission-line used to measure $z$ & $z$ & Flux [O\,{\sc ii}]$\lambda$3726\AA $^1$ & Flux [O\,{\sc ii}]$\lambda$3729\AA $^1$ \\
      \hline
       \multicolumn{5}{c}{BR1202-0725} \\
      \hline
      Quasar & [C\,{\sc ii}] & $4.6942$ & $\leq 4.3$ & $\leq 4.7$ \\
      SMG$^{\textrm{\dag}}$ & [C\,{\sc ii}] & $4.6915$ & $\leq 0.3$ & $\leq 0.5$ \\
      Ly$\alpha1^{\textrm{\dag}}$ & [C\,{\sc ii}] & $4.6950$ & $\leq 0.4$ & $\leq 0.7$ \\
      Ly$\alpha1^{\textrm{\dag}}$ & Ly$\alpha$ & $4.7026$ & $\leq 1.2$ & $\leq 0.3$ \\
      Ly$\alpha2^{\textrm{\dag}}$ & [C\,{\sc ii}] & $4.7055$ & $\leq 0.5$ & $\leq 0.6$ \\
      \hline
       \multicolumn{5}{c}{${\textrm{J1000+0234}}^{\textrm{\dag}}$} \\
      \hline
      SMG & Ly$\alpha$ & $4.547$ & $\leq 1.5$ & $\leq 1.3$ \\
      SMG & $^{12}$CO($4-3$) & $4.5423$ & $\leq 2.7$ & $\leq 1.1$ \\
      \hline
   \end{tabular}
    \begin{list}{}
      \centering
      \item $^1$ in units of $\times 10^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$.
      \item \dag \, flux limits are 5$\sigma$ level.
    \end{list}
  \label{redshift}
\end{table*}

\subsection{J1000+0234}

\begin{figure*}
\centering
\includegraphics[width=\textwidth]{figs/spec_J10.eps}
\caption{Black line: Integrated spectrum of J1000+0234 SMG, extracted inside the white squares shown in right panel of Fig. \ref{hst}, added by a constant ($2 \times 10^{-16}$ $\rm erg\,s^{-1}\,\,cm^{-2}\,\AA^{-1} \,$), for visual purposes. Red line: Sky spectra, which were derived as the median of the sky spectrum extracted from six different extraction regions located outside the overall structure of J1000+0234. Green line shows the residual between object and sky spectra and 1$\sigma$ uncertainty of the sky spectrum is displayed in blue line. Gray shaded area displays the 5$\sigma$ level. [O\,{\sc ii}] doublet wavelengths using redshifts from Ly$\alpha$ and $^{12}$CO($4-3$) emission-lines are represented by dashed and dot-dashed vertical black lines, respectively \citep[][see Table \ref{redshift}]{capak08,schinnerer08}. Dotted vertical lines correspond to sky lines wavelengths.}
\label{spec_j10}
\end{figure*}

The near-infrared structure of J1000+0234 is presented in right panel of Fig. \ref{hst} in a F160W {\it HST-WFC3} image, centered on the brightest region and covering the same FoV as our SINFONI integral field data. Fainter structures to the south-west and north-west appear within 1-2 arcsec. A foreground object ($z = 1.41$) is observed to the west of the SMG, as noted by \citet[][white arrow]{capak08}. White squares correspond to $0\farcs625 \times 0\farcs625$ apertures, that match our observational seeing (FWHM), where spectra were extracted. To optimize the signal from the SMG, the fluxes from the two regions identified as SMG have been combined. These are the brightest regions identified in the {\it HST-WFC3} image and where the emission from the [O\,{\sc ii}] would be expected in the {\it K}-band. Since the final SINFONI datacube was built using object and sky expositions, we establish the flux due to the sky emission (and its uncertainty) as the median value of the spaxels in six different extraction regions located outside the overall structure of J1000+0234 and avoiding other possible related features in the field. These regions have the same aperture size as the white squares in Fig. \ref{hst}.

The combined integrated spectrum of J1000+0234, extracted from the two apertures identified as SMG in Fig. \ref{hst}, is displayed in Fig. \ref{spec_j10}. Again there is no evidence of [O\,{\sc ii}] emission-lines, with no emission above the (5$\sigma$) sky uncertainties, and the sky lines are the only clear features present. The upper limits for the flux of the [O\,{\sc ii}] doublet is in the $\sim 2.8-3.8 \times 10^{-17}$ $\rm erg\,s^{-1}\,\,cm^{-2} \,$ range, depending on the redshift and line used (see Table \ref{redshift} for details).

\section{Discussions}
\label{analys}

\subsection{Star formation rates}
\label{SFRs}

We estimated the SFRs following the relation from \citet{kennicutt98}:

\begin{equation}
\textrm{SFR} (M_\odot\, yr^{-1}) = (1.4 \pm 0.4) \times 10^{-41} L_{\textrm{[O\,{\sc ii}] }} (\textrm{erg s}^{-1}),\end{equation}

\noindent
which make use of a Salpeter Initial Mass Function (IMF) with solar abundances. Table \ref{sfr} displays the upper limits of $L_{\textrm{[O\,{\sc ii}] }}$ and SFRs uncorrected from extinction for the objects of the two SMG systems, considering the redshifts mentioned in Table \ref{redshift} (lines used to measure the redshift are in parenthesis). Two approaches are shown: one using both of the [O{\sc ii}] emission-lines to estimate its luminosity, and another using the emission line less affected by the sky lines (thus with lower flux) to estimate the other, considering a line ratio of [O{\sc ii}]$3729/3726 = 1$ (assuming a $T \approx 15000\,$K and log\,$n_e = 2.5$) \footnote{SFRs should be multiplied by $0.6-0.7$ if applying another IMF such as \citet{kroupa01} or \citet{chabrier05} \citep{madau14}.}.

We find upper limits for the SFR rates of about $10-15\,$$\rm M_\odot\,yr^{-1} \,$ for the different components of the BR 1202-0725 system, and in the $\sim 30-40\,$$\rm M_\odot\,yr^{-1} \,$ range for J1000+0234. For the two SMGs, these SFR values are about two orders of magnitude lower than those derived from previous measurements, including mid-, far-infrared, and radio tracers. This clearly indicates that the amount of unobscured star formation in these systems as traced by the [O\,{\sc ii}] in emission is extremely low as compared with the global star formation. We find, for Ly$\alpha2$, SFRs of about one order of magnitude lower than those derived from \citet{carilli13}, using the FIR luminosity and the [C\,{\sc ii}] $158\mu$m line emission measurements. However, for Ly$\alpha1$, we find SFR upper limits that are in agreement with most of the estimates based on infrared tracers (see Table \ref{sfr}), indicating therefore that this system is less affected by dust internal extinction and is likely to have intrinsically low visual extinction values.

Assuming internal extincion plays the main role in explaining the differences between our [O\,{\sc ii}]-derived SFR and those derived from the infrared, we estimate in the following section the average visual extinction in the two SMGs and the two LAEs of BR1202-0725.

\begin{table*
   \centering
   \caption{\it Derived upper limits for the [O\,{\sc ii}] luminosities and corresponding SFR.}
   \begin{tabular}{c c c c c c}
      \hline \hline
      \multicolumn{6}{c}{BR1202-0725} \\
      \hline
      Object				& $\textrm{L}_{\textrm{[O{\sc ii}]}}^1$	& SFR$^1$ (this work)	& $\textrm{L}_{\textrm{[O{\sc ii}]}}^2$	& SFR$^2$ (this work)	& SFR$^3$ (literature) \\
					& [$\times 10^{42}\, \textrm{erg/s}$]		& [$\rm M_\odot\,yr^{-1} \,$]		& [$\times 10^{42}\, \textrm{erg/s}$]		& [$\rm M_\odot\,yr^{-1} \,$]		& [$\rm M_\odot\,yr^{-1} \,$] \\
      \hline
      SMG				& $\leq 1.8$					& $\leq 26$		& $\leq 0.7$					& $\leq 10$		& (1) \\
      Ly$\alpha1$ ([C\,{\sc ii}])	& $\leq 2.5$					& $\leq 34$		& $\leq 1.0$					& $\leq 14$		& (2) \\
      Ly$\alpha1$ (Ly$\alpha$)	& $\leq 3.4$					& $\leq 48$		& $\leq 0.7$					& $\leq 9$		& (2) \\
      Ly$\alpha2$			& $\leq 2.5$					& $\leq 35$		& $\leq 1.2$					& $\leq 16$		& (3) \\
      \hline
      \multicolumn{6}{c}{J1000+0234} \\
      \hline
      SMG (Ly$\alpha$)			& $\leq 5.8$					& $\leq 82$		& $\leq 2.8$					& $\leq 39$		& (4) \\
      SMG ($^{12}$CO($4-3$))		& $\leq 7.9$					& $\leq 111$		& $\leq 2.3$					& $\leq 32$		& (4) \\
     \hline
    \end{tabular}
    \begin{list}{}
      \centering
      \item $^1$ calculated using both [O{\sc ii}] emission lines; $^2$ calculated using the [O{\sc ii}] emission line less affected by sky emission lines and assuming a line ratio of [O{\sc ii}]$3729/3726 = 1$; $^3$ in parenthesis the emission-line or spectrum continuum used to calculate the SFR. All works use a Salpeter IMF with solar abundances.
      \item (1) [a] $2000$ (FIR); [b] $2600$ (FIR, SED model); [b] $6800\pm1000$ ([C\,{\sc ii}]); [b] $4000$ (radio, SED model).
      \item (2) [c] $15-54$ (UV); [d] $\approx 13$ (UV); [e] $\leq 36$ (FIR, SED model); [e] $19$ ([C\,{\sc ii}]).
      \item (3) [e] $170$ (FIR, SED model); [e] $70$ ([C\,{\sc ii}]).
      \item (4) [f] $1000-4000$ (FIR, SED model); [f] $3700\pm700$ (radio); [f] $2900\pm100$ ($3.6\mu$m band).
      \item Refs: [a] \citet{salome12}, [b] \citet{carniani13}, [c] \citet{fontana98}, [d] \citet{ohyama04}, [e] \citet{carilli13}, [f] \citet{capak08}
    \end{list}
    \label{sfr}
\end{table*}

\subsection{Internal extinction within the systems}
\label{reddening}

As already mentioned in the previous section, the most probable reason for the non detection of the [O{\sc ii}] emission-lines and estimated low SFR upper limits when compared with previous SFR values is extinction due to dust absorption in the SMGs. SFRs of $2000-7000\,$$\rm M_\odot\,yr^{-1} \,$ and $1000-4000\,$$\rm M_\odot\,yr^{-1} \,$ for BR1202-0725 and J1000+0234 SMGs would translate into $L_{\textrm{[O\,{\sc ii}]}}$ of $1.4-5.0 \times 10^{44}$ $\rm erg\,s^{-1}\,$ and $0.7-2.9 \times 10^{44}$ $\rm erg\,s^{-1}\,$, respectively, using the same \citet{kennicutt98} relation. Using the upper limits for the luminosities we estimate from the [O\,{\sc ii}] emission line less affected by the sky (fourth column in Table \ref{sfr}), the ratio of the [O\,{\sc ii}]-derived to IR-derived [O\,{\sc ii}] luminosities imply an extinction of at least $A_{\textrm{[O{\sc ii}]}} \ge 5.7-7.1$ mag and $3.5-5.2$ mag, for BR1202-0725 and J1000+0234 SMGs respectively. The resulting visual internal extinction is $A_V \ge 3.4 - 4.3$ mag and $2.1 - 3.1$ mag for a SMC reddening law \citep{gordon03}, or $A_V \ge 3.9 - 4.9$ and $2.4 - 3.6$ mag for a starburst reddening law \citep{calzetti00}, for BR1202-0725 and J1000+0234 SMGs respectively. These extinction values are comparable with the ones found in a low-z sample of U/LIRGS, measured over sizes of a few to several kpc \citep{piqueras13}, in agreement with other studies that find evidence that these objects are local conterparts of high-$z$ SMGs \citep{nesvadba07,menendez13}.

For the cases of the two LAEs of the BR1202-0725 system, Ly$\alpha1$ and Ly$\alpha2$, previous SFR values cover the $13-54\,$$\rm M_\odot\,yr^{-1} \,$ and $70-170\,$$\rm M_\odot\,yr^{-1} \,$ ranges (see Table \ref{sfr}), corresponding to luminosity upper limits of $L_{\textrm{[O{\sc ii}]}} \approx 0.9-3.8 \times 10^{42}$ $\rm erg\,s^{-1}\,$ and $5.0 - 12.1 \times 10^{42}$ $\rm erg\,s^{-1}\,$, respectively. The internal extincion in the visual is $A_V {\textrm{(SMC)}} \ge 0.2 - 0.9$ mag and $0.9 - 1.5$ mag, or $A_V {\textrm{(starburst)}} \ge 0.2 - 1.0$ mag and $1.1 - 1.7$ mag, for Ly$\alpha1$ and Ly$\alpha2$ respectively. It is important to emphasize here that the values of extinction in magnitudes we obtain are lower limits, since we derived them from flux upper limits, as show in Table \ref{redshift}.

\subsection{BR1202-0725: [O\,{\sc ii}] emission associated to Ly$\alpha$1?}
\label{ohta}

\citet{ohta00} reported [O\,{\sc ii}]$3727$ emission in a north-west companion of the quasar in BR1202-0725. This companion is located $\approx 2\farcs4$ northwest of the quasar, so probably relates to the object labeled as Ly$\alpha1$ here. Using a Cassegrain infrared camera (CISCO) attached to the Subaru Telescope, they obtained narrow-band imaging with a filter centered at $2.1196\,\mu$m with a FWHM of $0.0199\,\mu$m (the authors report a transmission between $2.113\,\mu$m and $2.126\,\mu$m ranging from 80\% to 88\%). The authors estimate a line flux of $2.5 \times 10^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$ within a circular aperture of $0\farcs93$ radius, with a continuum level 10 times lower.

According to our results, i.e. upper limit of 0.8 to 1.5 $\times$ 10$^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$ for the [O\,{\sc ii}] emission, we hypothesize that the flux reported by Ohta and collaborators could likely be the result of a non-optimal sky subtraction, producing a positive residual interpreted as [O\,{\sc ii}] in emission. To test this hypothesis, we took an square aperture with a $0\farcs75$ on a side, centered on Ly$\alpha1$, and integrated the residual spectrum over the entire spectral window from $2.113\,\mu$m to $2.126\,\mu$m. We obtain an integrated flux of $2.9\pm0.2 \times 10^{-17}\,$$\rm erg\,s^{-1}\,\,cm^{-2} \,$, in fair agreement with the value reported by \citet{ohta00}. The same kind of measurements were repeated at different positions within the SINFONI field-of-view, getting similar flux levels, within the $5 \sigma$ uncertainties. This supports the hypothesis of residual sky lines as the source of spurious emission. The residuals are likely due to two strong sky emission-lines, at $\approx \lambda 2.1177\,$ and $\approx \lambda 2.125 \,\mu$m (and another one, fainter, at $\approx \lambda 2.1232 \,\mu$m), that lay within the filter passband (Fig. \ref{spec_br}).

\section{Conclusions}
\label{conc}

In this work we presented deep SINFONI {\it K}-band integral field spectroscopy of two SMG systems at $z \sim 5$, BR1202-0725 and J1000+0234. BR1202-0725 consists of the SMG, a QSO and two LAEs while J1000+0234 appears to have a complex extended structure as traced by multiband optical and near-infrared imaging. Our main conclusions are:

\begin{enumerate}
\item The spectra extracted for all the objects, including the two LAEs, do not show any signature of the [O\,{\sc ii}] doublet, within the 5$\sigma$ sky uncertainties. The corresponding upper limits for the SFR in the two SMGs are two orders of magnitude below those derived from far-infrared measurements. The differences are explained as due to internal obscuration equivalent to an average visual internal extinction of, at least, $4.1$ mag and $2.8$ mag for BR1202-0725 and J1000+0234, respectively. These average high internal extinctions are similar to those measured in low-z U/LIRGs;
\item The SFR upper limit for Ly$\alpha2$ derived from the non-detection of the [O\,{\sc ii}] is at least one order of magnitude lower than that measured from the infrared, while the corresponding values for Ly$\alpha1$ are in fairly good agreement, suggesting a very low internal extinction in this LAE. We find an internal extinction of at least $0.6$ and $1.3$ magnitudes in the visual for Ly$\alpha1$ and Ly$\alpha2$, respectively;
\item Previous claims of [O\,{\sc ii}] emission associated to BR1202-0725 Ly$\alpha1$ based on narrow-band imaging are not confirmed by our SINFONI 2D spectroscopy. Residuals due to some of the sky emission lines within the filter bandpass could produce fluxes compatible with the [O\,{\sc ii}] flux reported previously by \citet{ohta00}.
\end{enumerate}

\begin{acknowledgements}
GSC and LC acknowledge support from CNPq special visitor fellowship PVE 313945/2013-6 under the Brazilian program Science without Borders. LC acknowledge support by grant AYA2012-32295. JPL acknowledge support by grant AYA-2012-39408-C02-1. LC acknowledge support by grant AYA2012-39408-C02-01 and AYA2015-68964. Based on observations collected at the European Organisation for Astronomical Research in the Southern Hemisphere, Chile, programm 093.A-0204A. Some of the data presented in this paper were obtained from the Multimission Archive at the Space Telescope Science Institute (MAST). GSC wants to thank Natacha Z. Dametto for fruitful discussions and assistance.
\end{acknowledgements}

\bibliographystyle{aa}

redpajama_set_name: RedPajamaArXiv


text:
Home > Ask Our Experts > When Did Royalty Begin Giving Recognition to Centenarians?
By: Chris Nickson - Updated: 5 Aug 2020 | *Discuss
My 5th great grandfather lived to the age of 106 and died in 1883. Did centenarians at that time receive recognition from Royalty and if so, are there any records that I could research?
(M.L, 12 May 2009)
Celebrations of age, especially milestones like a century, have long been part of humankind, although those who live to 100 and beyond are still few and far between. For many years the parties were restricted to family or community, but eventually the monarch did start the tradition of sending greetings to any British or Commonwealth citizen who reached 100 years of age, then again at 105 and every year thereafter.
Unfortunately for you, however, it didn't start until 1917, when George V sent out the first of these now-famous telegrams, which were delivered by bicycle, and actually written by a secretary.
These days it's not a telegram – those were phased out in 1982 – but a card, which, although not actually penned by the Queen, would seem to be coming from her, delivered in a special blue envelope, the card itself bearing the coat of arms, a photograph and a scan of the Queen's signature. These celebrations are also sent out for diamond wedding anniversaries – that's 60 years.
Whilst the first few years of the scheme saw very few of the congratulations sent, by now they number a few thousand a year as life expectancy increases. Originally the problem was compounded by the fact that centralised birth records – the birth certificates – didn't begin until 1837, meaning that for the first 20 years, people were reliant on old parish baptismal records to officially know a person's age.
It could well be that the early years of the 100th birthday telegrams saw some who were qualified not receiving them because of the inexactness of the records. Of course, none of this helps your researching, since there are no records to research.
For someone to live past 100 in the 19th century was indeed a rare event. To offer a standard, in 1901, if you were 65, then you could reasonably expect another 10.5 years of life if you were a man, 11.5 if you were a woman. However, that's assuming you reached 65, as most didn't; in the 19th century, life expectancy was just 37 years, meaning many didn't live past that age.
THE CENTENARIAN IS FOR SALE. This website is for sale [more info].
[Add a Comment]
@Dawn. Well done you - and happy 106th birthday to your grandmother.
TheCentenarian - 17-Jul-15 @ 10:20 AM
MY grandmother was born in Southern Ireland in 1909 and will celebrate her 106th birthday in three weeks time. Initially the Palace refused to send a 100th birthday card to her as they said she was Irish not English but with a little checking it became apparent that Eire was part of the UK in 1909, and until 1922 as far as I can gather and so the Palace did indeed send a card. She also received a card on her 105th birthday and will be receiving another very soon.
Dawn - 13-Jul-15 @ 1:40 PM
@Fran. Why not contact the palace yourself to ensure this can be looked into before the date. Her birth certificate will needed. Here's how to do it .
TheCentenarian - 17-Feb-15 @ 1:55 PM
Hi .. My aunt will be 100 years old on Christmas Eve this year. I am a little bit concerned that, as she was born in Southern Ireland in 1915 and not in the UK , she may miss out on her telegram from the Queen. She is of course a British Citizen. Do you think this might be a problem? If so how can I highlight this Her Name is Norah Barry 24-12-1915 Kindest regards Fran
Fran - 16-Feb-15 @ 8:56 AM
I have the original of a telegram sent to my great great grandmother on February 21st 1910She was Mrs Baker of 21 Black Griffin Lane Canterbury Kent. Telegram reads I am commanded by the King to congratulate you on having attained your hundredth birthday Knollys This was sent from Buckingham Palace on behalf on Edward VII Knollys was his Equerry
Tassie - 29-May-12 @ 5:54 AM
Obviously not a telegram but, according to the Huddersfeld Examiner, 17th January 1914, John Turner of Kirkburton near Huddersfield in the West Riding of Yorkshire, received a LETTER of congratulations on his centenary from King George V.It was signed by a certain Clive Wigram (Assistant Private Secretary and Equerry according to Wikipedia)
Robocart - 16-Oct-11 @ 1:33 AM
Share Your Story, Join the Discussion or Seek Advice...


Today's mass email complained bitterly about receiving mass email. In fact, it compared mass email to spam. The fact its sender was doing exactly the same thing was apparently not relevant. Actually, this too came from someone I know and like. Yet it was riddled with misconceptions.
One thing that was true was that there are indeed Unity-New Action people who work hard. You'd have to be a fool to say the union, dominated by Unity-New Action, never done anything of value or fought for anyone. I've got someone from Unity trying to help a colleague in deep trouble right now. I can't predict the outcome, but I've got faith this person will do all she can.
But there were other statements that were questionable, at best. The first was that this CL was Unity by choice. Now I'm certain this CL chose to be Unity. However, that choice is only extended by invitation. If they don't ask you, you're out. In fact, it wasn't until I started this blog that I even knew anything of Unity practices. I went 20 years without ever having much thought of it.
The other thing I questioned was the story of how this CL toiled away at a UFT job. In fact, UFT jobs are offered only to UFT-New Action members, with one solitary exception I know of. These jobs are clearly given as patronage, or James Eterno would be somewhere working for the union even as we speak. This is the same thing that governs who attends conventions. We speak of union democracy, and of voting, but who actually knows who the hell the 750 names on the ballot are? I've certainly known Unity chapter leaders who were largely asleep, and who did little aside from attending these conventions.
Somehow, the letter failed to mention the fact we'd supported mayoral control twice, enabled the miserable ATR brigade, failed to snag the pattern raise just about every other city union got between 08-10, or signed on to a junk science evaluation system that will almost certainly hurt working teachers. Perhaps the writer simply forgot.
Personally, if I could help the union, I'd do so without hesitation. I was once asked to train for a union job that never materialized. But I thought I could be of service, so I went for the training. I was quite surprised they even asked me to do that. I heard shocking things from other students in the classes I attended. I have no idea why they picked me, but I can assure you some people were not picked on merit.
In any case, I now expect to receive mass email from Unity every day of the election season protesting mass email. Few things could be more absurd.
And here's the bottom line to anyone sending mass email to chapter leaders--I very much doubt a single chapter leader anywhere will be swayed by campaign email.